<commit_before>//! The module containing building blocks for creating blockchains powered by the
//! Exonum framework.
//!
//! Services are the main extension point for the Exonum framework. To create your own service on
//! top of Exonum blockchain you need to define following things:
//!
//! - Define your own information schema.
//! - Create one or more types of messages using a macro `message!` and implement
//! `Transaction` trait for them.
//! - Create data structure that implements `Service` trait.
//! - Optionally you can write api handlers.
//!
//! You may follow the [`minibank`][tutoral] tutorial to get experience of programming your services.
//!
//! [tutoral]: https://github.com/DenisKolodin/exonum-doc/blob/currency-tutorial/src/home/cryptocurrency/intro.md

// TODO move to lib.rs
#![deny(missing_docs)]

use vec_map::VecMap;
use byteorder::{ByteOrder, LittleEndian};
use mount::Mount;

use std::sync::Arc;
use std::collections::BTreeMap;
use std::mem;
use std::fmt;
use std::panic;

use crypto::{self, Hash};
use messages::{RawMessage, Precommit, CONSENSUS as CORE_SERVICE};
use node::{State, TxPool};
use storage::{Patch, Database, Snapshot, Fork, Error};

pub use self::block::{Block, SCHEMA_MAJOR_VERSION};
pub use self::schema::{Schema, TxLocation, gen_prefix};
pub use self::genesis::GenesisConfig;
pub use self::config::{StoredConfiguration, ConsensusConfig};
pub use self::service::{Service, Transaction, ServiceContext, ApiContext};

mod block;
mod schema;
mod genesis;
mod service;
#[cfg(test)]
mod tests;

pub mod config;

/// Exonum blockchain instance with the concrete services set and data storage.
/// Only blockchains with the identical set of services and genesis block can be combined
/// into the single network.
pub struct Blockchain {
    db: Box<Database>,
    service_map: Arc<VecMap<Box<Service>>>,
}

impl Blockchain {
    /// Constructs a blockchain for the given `storage` and list of `services`.
    pub fn new(storage: Box<Database>, services: Vec<Box<Service>>) -> Blockchain {
        let mut service_map = VecMap::new();
        for service in services {
            let id = service.service_id() as usize;
            if service_map.contains_key(id) {
                panic!("Services have already contain service with id={}, please change it.",
                       id);
            }
            service_map.insert(id, service);
        }

        Blockchain {
            db: storage,
            service_map: Arc::new(service_map),
        }
    }

    /// Creates a readonly snapshot of the current storage state.
    pub fn snapshot(&self) -> Box<Snapshot> {
        self.db.snapshot()
    }

    /// Creates snapshot of the current storage state that can be later committed into storage
    /// via `merge` method.
    pub fn fork(&self) -> Fork {
        self.db.fork()
    }

    /// Tries to create a `Transaction` object from the given raw message.
    /// Raw message can be converted into `Transaction` object only
    /// if following conditions are met.
    ///
    /// - Blockchain has service with the `service_id` of given raw message.
    /// - Service can deserialize given raw message.
    pub fn tx_from_raw(&self, raw: RawMessage) -> Option<Box<Transaction>> {
        let id = raw.service_id() as usize;
        self.service_map
            .get(id)
            .and_then(|service| service.tx_from_raw(raw).ok())
    }

    /// Commits changes from the patch to the blockchain storage.
    /// See [`Fork`](../storage/struct.Fork.html) for details.
    pub fn merge(&mut self, patch: Patch) -> Result<(), Error> {
        self.db.merge(patch)
    }

    /// Returns the hash of latest committed block.
    ///
    /// # Panics
    ///
    /// - If the genesis block did not commited.
    pub fn last_hash(&self) -> Hash {
        Schema::new(&self.snapshot())
               .block_hashes_by_height()
               .last()
               .unwrap_or_else(Hash::default)
    }

    /// Returns the latest committed block.
    ///
    /// # Panics
    ///
    /// - If the genesis block did not commited.
    pub fn last_block(&self) -> Block {
        Schema::new(&self.snapshot()).last_block().unwrap()
    }

    /// Creates and commits the genesis block for the given genesis configuration.
    pub fn create_genesis_block(&mut self, cfg: GenesisConfig) -> Result<(), Error> {
        let mut config_propose = StoredConfiguration {
            previous_cfg_hash: Hash::zero(),
            actual_from: 0,
            validators: cfg.validators,
            consensus: cfg.consensus,
            services: BTreeMap::new(),
        };

        let patch = {
            let mut fork = self.fork();
            // Update service tables
            for (_, service) in self.service_map.iter() {
                let cfg = service.handle_genesis_block(&mut fork);
                let name = service.service_name();
                if config_propose.services.contains_key(name) {
                    panic!("Services have already contain service with name={}, please change it.",
                           name);
                }
                config_propose.services.insert(name.into(), cfg);
            }
            // Commit actual configuration
            {
                let mut schema = Schema::new(&mut fork);
                if schema.block_hash_by_height(0).is_some() {
                    // TODO create genesis block for MemoryDB and compare in hash with zero block
                    return Ok(());
                }
                schema.commit_configuration(config_propose);
            };
            self.merge(fork.into_patch())?;
            self.create_patch(0, 0, &[], &BTreeMap::new()).1
        };
        self.merge(patch)?;
        Ok(())
    }

    /// Helper function to map tuple (`u16`, `u16`) of service table coordinates
    /// to 32 byte value for use as `MerklePatriciaTable` key (it currently
    /// supports only fixed size keys). `hash` function is used to distribute
    /// keys uniformly (compared to padding).
    /// # Arguments
    ///
    /// * `service_id` - `service_id` as returned by instance of type of
    /// `Service` trait
    /// * `table_idx` - index of service table in `Vec`, returned by
    /// `state_hash` method of instance of type of `Service` trait
    // also, it was the first idea around, to use `hash`
    pub fn service_table_unique_key(service_id: u16, table_idx: usize) -> Hash {
        debug_assert!(table_idx <= u16::max_value() as usize);
        let size = mem::size_of::<u16>();
        let mut vec = vec![0; 2 * size];
        LittleEndian::write_u16(&mut vec[0..size], service_id);
        LittleEndian::write_u16(&mut vec[size..2 * size], table_idx as u16);
        crypto::hash(&vec)
    }

    /// Executes the given transactions from pool.
    /// Then it collects the resulting changes from the current storage state and returns them
    /// with the hash of resulting block.
    pub fn create_patch(&self,
                        proposer_id: u16,
                        height: u64,
                        tx_hashes: &[Hash],
                        pool: &TxPool)
                        -> (Hash, Patch) {
        // Create fork
        let mut fork = self.fork();

        let block_hash = {
            // Get last hash
            let last_hash = self.last_hash();
            // Save & execute transactions
            for (index, hash) in tx_hashes.iter().enumerate() {
                let tx = &pool[hash];

                fork.checkpoint();

                let r = panic::catch_unwind(panic::AssertUnwindSafe(|| {
                    tx.execute(&mut fork);
                }));

                match r {
                    Ok(..) => fork.commit(),
                    Err(err) => {
                        if err.is::<Error>() {
                            // Continue panic unwind if the reason is StorageError
                            panic::resume_unwind(err);
                        }
                        fork.rollback();
                        error!("Transaction execution failed: {:?}, reason: {:?}", tx, err);
                    }
                }

                let mut schema = Schema::new(&mut fork);
                schema.transactions_mut().put(hash, tx.raw().clone());
                schema.block_txs_mut(height).push(*hash);
                let location = TxLocation::new(height, index as u64);
                schema.tx_location_by_tx_hash_mut().put(hash, location);
            }

            // Get tx & state hash
            let (tx_hash, state_hash) = {

                let state_hashes = {
                    let schema = Schema::new(&fork);

                    let vec_core_state = schema.core_state_hash();
                    let mut state_hashes = Vec::new();

                    for (idx, core_table_hash) in vec_core_state.into_iter().enumerate() {
                        let key = Blockchain::service_table_unique_key(CORE_SERVICE, idx);
                        state_hashes.push((key, core_table_hash));
                    }

                    for service in self.service_map.values() {
                        let service_id = service.service_id();
                        let vec_service_state = service.state_hash(&fork);
                        for (idx, service_table_hash) in vec_service_state.into_iter().enumerate() {
                            let key = Blockchain::service_table_unique_key(service_id, idx);
                            state_hashes.push((key, service_table_hash));
                        }
                    }

                    state_hashes
                };

                let mut schema = Schema::new(&mut fork);

                let state_hash = {
                    let mut sum_table = schema.state_hash_aggregator_mut();
                    for (key, hash) in state_hashes {
                        sum_table.put(&key, hash)
                    }
                    sum_table.root_hash()
                };

                let tx_hash = schema.block_txs(height).root_hash();

                (tx_hash, state_hash)
            };

            // Create block
            let block = Block::new(SCHEMA_MAJOR_VERSION,
                                   proposer_id,
                                   height,
                                   tx_hashes.len() as u32,
                                   &last_hash,
                                   &tx_hash,
                                   &state_hash);
            trace!("execute block = {:?}", block);
            // Eval block hash
            let block_hash = block.hash();
            // Update height
            let mut schema = Schema::new(&mut fork);
            schema.block_hashes_by_height_mut().push(block_hash);
            // Save block
            schema.blocks_mut().put(&block_hash, block);

            block_hash
        };

        (block_hash, fork.into_patch())
    }

    /// Commits to the storage block that proposes by node `State`.
    /// After that invokes `handle_commit` for each service in order of their identifiers
    /// and returns the list of transactions which which were created by the `handle_commit` event.
    #[cfg_attr(feature="flame_profile", flame)]
    pub fn commit<'a, I>(&mut self,
                         state: &mut State,
                         block_hash: Hash,
                         precommits: I)
                         -> Result<Vec<Box<Transaction>>, Error>
        where I: Iterator<Item = &'a Precommit>
    {
        let (patch, txs) = {
            let mut fork = {
                let patch = state.block(&block_hash).unwrap().patch();
                let mut fork = self.db.fork();
                fork.merge(patch.clone()); // FIXME: avoid cloning here
                fork
            };

            {
                let mut schema = Schema::new(&mut fork);
                for precommit in precommits {
                    schema.precommits_mut(&block_hash).push(precommit.clone());
                }

                state.update_config(schema.actual_configuration());
            }

            let transactions = {
                let mut ctx = ServiceContext::new(state, &fork);
                for service in self.service_map.values() {
                    service.handle_commit(&mut ctx);
                };
                ctx.transactions()
            };

            (fork.into_patch(), transactions)
        };
        self.merge(patch)?;
        Ok(txs)
    }

    /// Returns `Mount` object that aggregates public api handlers.
    pub fn mount_public_api(&self, context: &ApiContext) -> Mount {
        let mut mount = Mount::new();
        for service in self.service_map.values() {
            if let Some(handler) = service.public_api_handler(context) {
                mount.mount(service.service_name(), handler);
            }
        }
        mount
    }

    /// Returns `Mount` object that aggregates private api handlers.
    pub fn mount_private_api(&self, context: &ApiContext) -> Mount {
        let mut mount = Mount::new();
        for service in self.service_map.values() {
            if let Some(handler) = service.private_api_handler(context) {
                mount.mount(service.service_name(), handler);
            }
        }
        mount
    }
}

impl fmt::Debug for Blockchain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Blockchain(..)")
    }
}

impl Clone for Blockchain {
    fn clone(&self) -> Blockchain {
        Blockchain {
            db: self.db.clone(),
            service_map: self.service_map.clone()
        }
    }
}
<commit_msg>Grammar fixes.<commit_after>//! The module containing building blocks for creating blockchains powered by the
//! Exonum framework.
//!
//! Services are the main extension point for the Exonum framework. To create your own service on
//! top of Exonum blockchain you need to define following things:
//!
//! - Define your own information schema.
//! - Create one or more types of messages using a macro `message!` and implement
//! `Transaction` trait for them.
//! - Create data structure that implements `Service` trait.
//! - Optionally you can write api handlers.
//!
//! You may follow the [`minibank`][tutoral] tutorial to get experience of programming your services.
//!
//! [tutoral]: https://github.com/DenisKolodin/exonum-doc/blob/currency-tutorial/src/home/cryptocurrency/intro.md

// TODO move to lib.rs
#![deny(missing_docs)]

use vec_map::VecMap;
use byteorder::{ByteOrder, LittleEndian};
use mount::Mount;

use std::sync::Arc;
use std::collections::BTreeMap;
use std::mem;
use std::fmt;
use std::panic;

use crypto::{self, Hash};
use messages::{RawMessage, Precommit, CONSENSUS as CORE_SERVICE};
use node::{State, TxPool};
use storage::{Patch, Database, Snapshot, Fork, Error};

pub use self::block::{Block, SCHEMA_MAJOR_VERSION};
pub use self::schema::{Schema, TxLocation, gen_prefix};
pub use self::genesis::GenesisConfig;
pub use self::config::{StoredConfiguration, ConsensusConfig};
pub use self::service::{Service, Transaction, ServiceContext, ApiContext};

mod block;
mod schema;
mod genesis;
mod service;
#[cfg(test)]
mod tests;

pub mod config;

/// Exonum blockchain instance with the concrete services set and data storage.
/// Only blockchains with the identical set of services and genesis block can be combined
/// into the single network.
pub struct Blockchain {
    db: Box<Database>,
    service_map: Arc<VecMap<Box<Service>>>,
}

impl Blockchain {
    /// Constructs a blockchain for the given `storage` and list of `services`.
    pub fn new(storage: Box<Database>, services: Vec<Box<Service>>) -> Blockchain {
        let mut service_map = VecMap::new();
        for service in services {
            let id = service.service_id() as usize;
            if service_map.contains_key(id) {
                panic!("Services have already contain service with id={}, please change it.",
                       id);
            }
            service_map.insert(id, service);
        }

        Blockchain {
            db: storage,
            service_map: Arc::new(service_map),
        }
    }

    /// Creates a readonly snapshot of the current storage state.
    pub fn snapshot(&self) -> Box<Snapshot> {
        self.db.snapshot()
    }

    /// Creates snapshot of the current storage state that can be later committed into storage
    /// via `merge` method.
    pub fn fork(&self) -> Fork {
        self.db.fork()
    }

    /// Tries to create a `Transaction` object from the given raw message.
    /// Raw message can be converted into `Transaction` object only
    /// if following conditions are met.
    ///
    /// - Blockchain has service with the `service_id` of given raw message.
    /// - Service can deserialize given raw message.
    pub fn tx_from_raw(&self, raw: RawMessage) -> Option<Box<Transaction>> {
        let id = raw.service_id() as usize;
        self.service_map
            .get(id)
            .and_then(|service| service.tx_from_raw(raw).ok())
    }

    /// Commits changes from the patch to the blockchain storage.
    /// See [`Fork`](../storage/struct.Fork.html) for details.
    pub fn merge(&mut self, patch: Patch) -> Result<(), Error> {
        self.db.merge(patch)
    }

    /// Returns the hash of latest committed block.
    ///
    /// # Panics
    ///
    /// - If the genesis block was not committed.
    pub fn last_hash(&self) -> Hash {
        Schema::new(&self.snapshot())
               .block_hashes_by_height()
               .last()
               .unwrap_or_else(Hash::default)
    }

    /// Returns the latest committed block.
    ///
    /// # Panics
    ///
    /// - If the genesis block was not committed.
    pub fn last_block(&self) -> Block {
        Schema::new(&self.snapshot()).last_block().unwrap()
    }

    /// Creates and commits the genesis block for the given genesis configuration.
    pub fn create_genesis_block(&mut self, cfg: GenesisConfig) -> Result<(), Error> {
        let mut config_propose = StoredConfiguration {
            previous_cfg_hash: Hash::zero(),
            actual_from: 0,
            validators: cfg.validators,
            consensus: cfg.consensus,
            services: BTreeMap::new(),
        };

        let patch = {
            let mut fork = self.fork();
            // Update service tables
            for (_, service) in self.service_map.iter() {
                let cfg = service.handle_genesis_block(&mut fork);
                let name = service.service_name();
                if config_propose.services.contains_key(name) {
                    panic!("Services have already contain service with name={}, please change it.",
                           name);
                }
                config_propose.services.insert(name.into(), cfg);
            }
            // Commit actual configuration
            {
                let mut schema = Schema::new(&mut fork);
                if schema.block_hash_by_height(0).is_some() {
                    // TODO create genesis block for MemoryDB and compare in hash with zero block
                    return Ok(());
                }
                schema.commit_configuration(config_propose);
            };
            self.merge(fork.into_patch())?;
            self.create_patch(0, 0, &[], &BTreeMap::new()).1
        };
        self.merge(patch)?;
        Ok(())
    }

    /// Helper function to map tuple (`u16`, `u16`) of service table coordinates
    /// to 32 byte value for use as `MerklePatriciaTable` key (it currently
    /// supports only fixed size keys). `hash` function is used to distribute
    /// keys uniformly (compared to padding).
    /// # Arguments
    ///
    /// * `service_id` - `service_id` as returned by instance of type of
    /// `Service` trait
    /// * `table_idx` - index of service table in `Vec`, returned by
    /// `state_hash` method of instance of type of `Service` trait
    // also, it was the first idea around, to use `hash`
    pub fn service_table_unique_key(service_id: u16, table_idx: usize) -> Hash {
        debug_assert!(table_idx <= u16::max_value() as usize);
        let size = mem::size_of::<u16>();
        let mut vec = vec![0; 2 * size];
        LittleEndian::write_u16(&mut vec[0..size], service_id);
        LittleEndian::write_u16(&mut vec[size..2 * size], table_idx as u16);
        crypto::hash(&vec)
    }

    /// Executes the given transactions from pool.
    /// Then it collects the resulting changes from the current storage state and returns them
    /// with the hash of resulting block.
    pub fn create_patch(&self,
                        proposer_id: u16,
                        height: u64,
                        tx_hashes: &[Hash],
                        pool: &TxPool)
                        -> (Hash, Patch) {
        // Create fork
        let mut fork = self.fork();

        let block_hash = {
            // Get last hash
            let last_hash = self.last_hash();
            // Save & execute transactions
            for (index, hash) in tx_hashes.iter().enumerate() {
                let tx = &pool[hash];

                fork.checkpoint();

                let r = panic::catch_unwind(panic::AssertUnwindSafe(|| {
                    tx.execute(&mut fork);
                }));

                match r {
                    Ok(..) => fork.commit(),
                    Err(err) => {
                        if err.is::<Error>() {
                            // Continue panic unwind if the reason is StorageError
                            panic::resume_unwind(err);
                        }
                        fork.rollback();
                        error!("Transaction execution failed: {:?}, reason: {:?}", tx, err);
                    }
                }

                let mut schema = Schema::new(&mut fork);
                schema.transactions_mut().put(hash, tx.raw().clone());
                schema.block_txs_mut(height).push(*hash);
                let location = TxLocation::new(height, index as u64);
                schema.tx_location_by_tx_hash_mut().put(hash, location);
            }

            // Get tx & state hash
            let (tx_hash, state_hash) = {

                let state_hashes = {
                    let schema = Schema::new(&fork);

                    let vec_core_state = schema.core_state_hash();
                    let mut state_hashes = Vec::new();

                    for (idx, core_table_hash) in vec_core_state.into_iter().enumerate() {
                        let key = Blockchain::service_table_unique_key(CORE_SERVICE, idx);
                        state_hashes.push((key, core_table_hash));
                    }

                    for service in self.service_map.values() {
                        let service_id = service.service_id();
                        let vec_service_state = service.state_hash(&fork);
                        for (idx, service_table_hash) in vec_service_state.into_iter().enumerate() {
                            let key = Blockchain::service_table_unique_key(service_id, idx);
                            state_hashes.push((key, service_table_hash));
                        }
                    }

                    state_hashes
                };

                let mut schema = Schema::new(&mut fork);

                let state_hash = {
                    let mut sum_table = schema.state_hash_aggregator_mut();
                    for (key, hash) in state_hashes {
                        sum_table.put(&key, hash)
                    }
                    sum_table.root_hash()
                };

                let tx_hash = schema.block_txs(height).root_hash();

                (tx_hash, state_hash)
            };

            // Create block
            let block = Block::new(SCHEMA_MAJOR_VERSION,
                                   proposer_id,
                                   height,
                                   tx_hashes.len() as u32,
                                   &last_hash,
                                   &tx_hash,
                                   &state_hash);
            trace!("execute block = {:?}", block);
            // Eval block hash
            let block_hash = block.hash();
            // Update height
            let mut schema = Schema::new(&mut fork);
            schema.block_hashes_by_height_mut().push(block_hash);
            // Save block
            schema.blocks_mut().put(&block_hash, block);

            block_hash
        };

        (block_hash, fork.into_patch())
    }

    /// Commits to the storage block that proposes by node `State`.
    /// After that invokes `handle_commit` for each service in order of their identifiers
    /// and returns the list of transactions which which were created by the `handle_commit` event.
    #[cfg_attr(feature="flame_profile", flame)]
    pub fn commit<'a, I>(&mut self,
                         state: &mut State,
                         block_hash: Hash,
                         precommits: I)
                         -> Result<Vec<Box<Transaction>>, Error>
        where I: Iterator<Item = &'a Precommit>
    {
        let (patch, txs) = {
            let mut fork = {
                let patch = state.block(&block_hash).unwrap().patch();
                let mut fork = self.db.fork();
                fork.merge(patch.clone()); // FIXME: avoid cloning here
                fork
            };

            {
                let mut schema = Schema::new(&mut fork);
                for precommit in precommits {
                    schema.precommits_mut(&block_hash).push(precommit.clone());
                }

                state.update_config(schema.actual_configuration());
            }

            let transactions = {
                let mut ctx = ServiceContext::new(state, &fork);
                for service in self.service_map.values() {
                    service.handle_commit(&mut ctx);
                };
                ctx.transactions()
            };

            (fork.into_patch(), transactions)
        };
        self.merge(patch)?;
        Ok(txs)
    }

    /// Returns `Mount` object that aggregates public api handlers.
    pub fn mount_public_api(&self, context: &ApiContext) -> Mount {
        let mut mount = Mount::new();
        for service in self.service_map.values() {
            if let Some(handler) = service.public_api_handler(context) {
                mount.mount(service.service_name(), handler);
            }
        }
        mount
    }

    /// Returns `Mount` object that aggregates private api handlers.
    pub fn mount_private_api(&self, context: &ApiContext) -> Mount {
        let mut mount = Mount::new();
        for service in self.service_map.values() {
            if let Some(handler) = service.private_api_handler(context) {
                mount.mount(service.service_name(), handler);
            }
        }
        mount
    }
}

impl fmt::Debug for Blockchain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Blockchain(..)")
    }
}

impl Clone for Blockchain {
    fn clone(&self) -> Blockchain {
        Blockchain {
            db: self.db.clone(),
            service_map: self.service_map.clone()
        }
    }
}
<|endoftext|>


<commit_before>#[test]
fn num_cards() {
    let deck = super::Deck::new();
    assert_eq!(deck.cards.len(), 52);
}

#[test]
fn draw_card() {
    let mut deck = super::Deck::new();
    let top_card_value = deck.cards.get(51).unwrap().value.clone();
    let top_card_suit = deck.cards.get(51).unwrap().suit.clone();
    let card = deck.draw();
    assert_eq!(deck.cards.len(), 51);

    assert!(card.value != ::card::Value::None);
    assert!(card.suit != ::card::Suit::None);

    assert_eq!(card.value, top_card_value);
    assert_eq!(card.suit, top_card_suit);
}
<commit_msg>Added unit test for drawing too many cards<commit_after>#[test]
fn num_cards() {
    let deck = super::Deck::new();
    assert_eq!(deck.cards.len(), 52);
}

#[test]
fn draw_one_card() {
    let mut deck = super::Deck::new();
    let top_card_value = deck.cards.get(51).unwrap().value.clone();
    let top_card_suit = deck.cards.get(51).unwrap().suit.clone();
    let card = deck.draw().unwrap();
    assert_eq!(deck.cards.len(), 51);

    assert!(card.value != ::card::Value::None);
    assert!(card.suit != ::card::Suit::None);

    assert_eq!(card.value, top_card_value);
    assert_eq!(card.suit, top_card_suit);
}

#[test]
fn draw_too_many_cards() {
    let mut deck = super::Deck::new();
    for i in 0..53 {
        let exists = match deck.draw() {
            None    => false,
            Some(_) => true
        };
        if i < 52 {
            assert!(exists);
        } else {
            assert!(!exists);
        }
    }
}
<|endoftext|>


<commit_before>use std::ops::Range;

/// A (set of) dimensions in an array. A `Dimensions` object carries informations
/// about its size, and how to convert a multi-dimensional index to a linear
/// index.
pub trait Dimensions: Clone + PartialEq {
    /// The type to use to index this dimension
    type Index: Copy;
    /// Convert an index to a linear offset for this dimension size
    fn offset(&self, index: Self::Index) -> usize;
    /// Get the number of elements in this dimension
    fn size(&self) -> usize;
}

impl Dimensions for usize {
    type Index = usize;

    #[inline(always)]
    fn offset(&self, index: usize) -> usize {
        assert!(index < *self);
        index
    }

    #[inline(always)]
    fn size(&self) -> usize {
        *self
    }
}

impl Dimensions for Range<usize> {
    type Index = usize;

    #[inline(always)]
    fn offset(&self, index: usize) -> usize {
        assert!(self.start <= index && index < self.end);
        index - self.start
    }

    #[inline(always)]
    fn size(&self) -> usize {
        self.end - self.start
    }
}

impl Dimensions for Range<i32> {
    type Index = i32;

    #[inline(always)]
    fn offset(&self, index: i32) -> usize {
        assert!(self.start <= index && index < self.end);
        assert!(index - self.start > 0);
        (index - self.start) as usize
    }

    #[inline(always)]
    fn size(&self) -> usize {
        (self.end - self.start) as usize
    }
}

impl<A, B> Dimensions for (A, B) where A: Dimensions, B: Dimensions {
    type Index = (A::Index, B::Index);

    #[inline(always)]
    fn offset(&self, index: Self::Index) -> usize {
        self.1.size() * self.0.offset(index.0) + self.1.offset(index.1)
    }

    #[inline(always)]
    fn size(&self) -> usize {
        self.0.size() * self.1.size()
    }
}

impl<A, B, C> Dimensions for (A, B, C) where A: Dimensions, B: Dimensions, C: Dimensions {
    type Index = (A::Index, B::Index, C::Index);

    #[inline(always)]
    fn offset(&self, index: Self::Index) -> usize {
        self.2.offset(index.2) + self.2.size() * (self.1.offset(index.1) + self.1.size() * self.0.offset(index.0))
    }

    #[inline(always)]
    fn size(&self) -> usize {
        self.0.size() * self.1.size() * self.2.size()
    }
}

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

    mod urange {
        use super::*;
        #[test]
        fn offset() {
            let dim = 4..10;
            assert_eq!(dim.size(), 6);
            assert_eq!(dim.offset(5), 1);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim() {
            let dim = 4..10;
            dim.offset(11);
        }

        #[test]
        #[should_panic]
        fn smaller_than_dim() {
            let dim = 4..10;
            dim.offset(2);
        }
    }

    mod irange {
        use super::*;
        #[test]
        fn offset() {
            let dim = -4..10;
            assert_eq!(dim.size(), 14);
            assert_eq!(dim.offset(5), 9);
            assert_eq!(dim.offset(-3), 1);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim() {
            let dim = -4..10;
            dim.offset(11);
        }

        #[test]
        #[should_panic]
        fn smaller_than_dim() {
            let dim = -4..10;
            dim.offset(-6);
        }
    }

    mod dim1 {
        use super::*;
        #[test]
        fn offset() {
            let dim = 8;
            assert_eq!(dim.size(), 8);
            assert_eq!(dim.offset(3), 3);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim() {
            let dim = 4;
            dim.offset(5);
        }
    }

    mod dim2 {
        use super::*;
        #[test]
        fn offset() {
            let dim = (2, 3);
            assert_eq!(dim.size(), 6);

            assert_eq!(dim.offset((0, 1)), 1);
            assert_eq!(dim.offset((1, 0)), 3);

            assert_eq!(dim.offset((1, 2)), 5);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_one() {
            let dim = (8, 6);
            dim.offset((8, 4));
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_two() {
            let dim = (8, 6);
            dim.offset((7, 8));
        }
    }

    mod dim3 {
        use super::*;
        #[test]
        fn offset() {
            let dim = (2, 2, 5);
            assert_eq!(dim.size(), 20);

            assert_eq!(dim.offset((0, 0, 1)), 1);
            assert_eq!(dim.offset((0, 1, 0)), 5);
            assert_eq!(dim.offset((1, 0, 0)), 10);

            assert_eq!(dim.offset((1, 1, 4)), 19);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_one() {
            let dim = (2, 6, 6);
            dim.offset((9, 4, 3));
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_two() {
            let dim = (2, 6, 6);
            dim.offset((1, 8, 3));
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_three() {
            let dim = (2, 6, 6);
            dim.offset((1, 4, 12));
        }
    }
}
<commit_msg>Better out of bounds messages<commit_after>use std::ops::Range;

/// A (set of) dimensions in an array. A `Dimensions` object carries informations
/// about its size, and how to convert a multi-dimensional index to a linear
/// index.
pub trait Dimensions: Clone + PartialEq {
    /// The type to use to index this dimension
    type Index: Copy;
    /// Convert an index to a linear offset for this dimension size
    fn offset(&self, index: Self::Index) -> usize;
    /// Get the number of elements in this dimension
    fn size(&self) -> usize;
}

impl Dimensions for usize {
    type Index = usize;

    #[inline(always)]
    fn offset(&self, index: usize) -> usize {
        assert!(index < *self, "index out of bound: len is {} but index is {}", self, index);
        index
    }

    #[inline(always)]
    fn size(&self) -> usize {
        *self
    }
}

impl Dimensions for Range<usize> {
    type Index = usize;

    #[inline(always)]
    fn offset(&self, index: usize) -> usize {
        assert!(self.start <= index && index < self.end,
                "index out of bound: range is ({}..{}) but index is {}",
                self.start, self.end, index);
        index - self.start
    }

    #[inline(always)]
    fn size(&self) -> usize {
        self.end - self.start
    }
}

impl Dimensions for Range<i32> {
    type Index = i32;

    #[inline(always)]
    fn offset(&self, index: i32) -> usize {
        assert!(self.start <= index && index < self.end,
                "index out of bound: range is ({}..{}) but index is {}",
                self.start, self.end, index);
        (index - self.start) as usize
    }

    #[inline(always)]
    fn size(&self) -> usize {
        (self.end - self.start) as usize
    }
}

impl<A, B> Dimensions for (A, B) where A: Dimensions, B: Dimensions {
    type Index = (A::Index, B::Index);

    #[inline(always)]
    fn offset(&self, index: Self::Index) -> usize {
        self.1.size() * self.0.offset(index.0) + self.1.offset(index.1)
    }

    #[inline(always)]
    fn size(&self) -> usize {
        self.0.size() * self.1.size()
    }
}

impl<A, B, C> Dimensions for (A, B, C) where A: Dimensions, B: Dimensions, C: Dimensions {
    type Index = (A::Index, B::Index, C::Index);

    #[inline(always)]
    fn offset(&self, index: Self::Index) -> usize {
        self.2.offset(index.2) + self.2.size() * (self.1.offset(index.1) + self.1.size() * self.0.offset(index.0))
    }

    #[inline(always)]
    fn size(&self) -> usize {
        self.0.size() * self.1.size() * self.2.size()
    }
}

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

    mod urange {
        use super::*;
        #[test]
        fn offset() {
            let dim = 4..10;
            assert_eq!(dim.size(), 6);
            assert_eq!(dim.offset(5), 1);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim() {
            let dim = 4..10;
            dim.offset(11);
        }

        #[test]
        #[should_panic]
        fn smaller_than_dim() {
            let dim = 4..10;
            dim.offset(2);
        }
    }

    mod irange {
        use super::*;
        #[test]
        fn offset() {
            let dim = -4..10;
            assert_eq!(dim.size(), 14);
            assert_eq!(dim.offset(5), 9);
            assert_eq!(dim.offset(-3), 1);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim() {
            let dim = -4..10;
            dim.offset(11);
        }

        #[test]
        #[should_panic]
        fn smaller_than_dim() {
            let dim = -4..10;
            dim.offset(-6);
        }
    }

    mod dim1 {
        use super::*;
        #[test]
        fn offset() {
            let dim = 8;
            assert_eq!(dim.size(), 8);
            assert_eq!(dim.offset(3), 3);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim() {
            let dim = 4;
            dim.offset(5);
        }
    }

    mod dim2 {
        use super::*;
        #[test]
        fn offset() {
            let dim = (2, 3);
            assert_eq!(dim.size(), 6);

            assert_eq!(dim.offset((0, 1)), 1);
            assert_eq!(dim.offset((1, 0)), 3);

            assert_eq!(dim.offset((1, 2)), 5);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_one() {
            let dim = (8, 6);
            dim.offset((8, 4));
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_two() {
            let dim = (8, 6);
            dim.offset((7, 8));
        }
    }

    mod dim3 {
        use super::*;
        #[test]
        fn offset() {
            let dim = (2, 2, 5);
            assert_eq!(dim.size(), 20);

            assert_eq!(dim.offset((0, 0, 1)), 1);
            assert_eq!(dim.offset((0, 1, 0)), 5);
            assert_eq!(dim.offset((1, 0, 0)), 10);

            assert_eq!(dim.offset((1, 1, 4)), 19);
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_one() {
            let dim = (2, 6, 6);
            dim.offset((9, 4, 3));
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_two() {
            let dim = (2, 6, 6);
            dim.offset((1, 8, 3));
        }

        #[test]
        #[should_panic]
        fn bigger_than_dim_three() {
            let dim = (2, 6, 6);
            dim.offset((1, 4, 12));
        }
    }
}
<|endoftext|>


<commit_before>//! This module implements some validity checks for attributes.
//! In particular it verifies that `#[inline]` and `#[repr]` attributes are
//! attached to items that actually support them and if there are
//! conflicts between multiple such attributes attached to the same
//! item.

use crate::hir;
use crate::hir::def_id::DefId;
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
use crate::ty::TyCtxt;
use crate::ty::query::Providers;

use std::fmt::{self, Display};
use syntax::{attr, symbol::sym};
use syntax_pos::Span;

#[derive(Copy, Clone, PartialEq)]
pub(crate) enum Target {
    ExternCrate,
    Use,
    Static,
    Const,
    Fn,
    Closure,
    Mod,
    ForeignMod,
    GlobalAsm,
    TyAlias,
    OpaqueTy,
    Enum,
    Struct,
    Union,
    Trait,
    TraitAlias,
    Impl,
    Expression,
    Statement,
}

impl Display for Target {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", match *self {
            Target::ExternCrate => "extern crate",
            Target::Use => "use",
            Target::Static => "static item",
            Target::Const => "constant item",
            Target::Fn => "function",
            Target::Closure => "closure",
            Target::Mod => "module",
            Target::ForeignMod => "foreign module",
            Target::GlobalAsm => "global asm",
            Target::TyAlias => "type alias",
            Target::OpaqueTy => "opaque type",
            Target::Enum => "enum",
            Target::Struct => "struct",
            Target::Union => "union",
            Target::Trait => "trait",
            Target::TraitAlias => "trait alias",
            Target::Impl => "item",
            Target::Expression => "expression",
            Target::Statement => "statement",
        })
    }
}

impl Target {
    pub(crate) fn from_item(item: &hir::Item) -> Target {
        match item.kind {
            hir::ItemKind::ExternCrate(..) => Target::ExternCrate,
            hir::ItemKind::Use(..) => Target::Use,
            hir::ItemKind::Static(..) => Target::Static,
            hir::ItemKind::Const(..) => Target::Const,
            hir::ItemKind::Fn(..) => Target::Fn,
            hir::ItemKind::Mod(..) => Target::Mod,
            hir::ItemKind::ForeignMod(..) => Target::ForeignMod,
            hir::ItemKind::GlobalAsm(..) => Target::GlobalAsm,
            hir::ItemKind::TyAlias(..) => Target::TyAlias,
            hir::ItemKind::OpaqueTy(..) => Target::OpaqueTy,
            hir::ItemKind::Enum(..) => Target::Enum,
            hir::ItemKind::Struct(..) => Target::Struct,
            hir::ItemKind::Union(..) => Target::Union,
            hir::ItemKind::Trait(..) => Target::Trait,
            hir::ItemKind::TraitAlias(..) => Target::TraitAlias,
            hir::ItemKind::Impl(..) => Target::Impl,
        }
    }
}

struct CheckAttrVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
}

impl CheckAttrVisitor<'tcx> {
    /// Checks any attribute.
    fn check_attributes(&self, item: &hir::Item, target: Target) {
        let mut is_valid = true;
        for attr in &item.attrs {
            is_valid &= if attr.check_name(sym::inline) {
                self.check_inline(attr, &item.span, target)
            } else if attr.check_name(sym::non_exhaustive) {
                self.check_non_exhaustive(attr, item, target)
            } else if attr.check_name(sym::marker) {
                self.check_marker(attr, item, target)
            } else if attr.check_name(sym::target_feature) {
                self.check_target_feature(attr, item, target)
            } else if attr.check_name(sym::track_caller) {
                self.check_track_caller(attr, &item, target)
            } else {
                true
            };
        }

        if !is_valid {
            return;
        }

        if target == Target::Fn {
            self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(item.hir_id));
        }

        self.check_repr(item, target);
        self.check_used(item, target);
    }

    /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
    fn check_inline(&self, attr: &hir::Attribute, span: &Span, target: Target) -> bool {
        if target != Target::Fn && target != Target::Closure {
            struct_span_err!(self.tcx.sess,
                             attr.span,
                             E0518,
                             "attribute should be applied to function or closure")
                .span_label(*span, "not a function or closure")
                .emit();
            false
        } else {
            true
        }
    }

    /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
    fn check_track_caller(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) -> bool {
        if target != Target::Fn {
            struct_span_err!(
                self.tcx.sess,
                attr.span,
                E0739,
                "attribute should be applied to function"
            )
            .span_label(item.span, "not a function")
            .emit();
            false
        } else if attr::contains_name(&item.attrs, sym::naked) {
            struct_span_err!(
                self.tcx.sess,
                attr.span,
                E0736,
                "cannot use `#[track_caller]` with `#[naked]`",
            )
            .emit();
            false
        } else {
            true
        }
    }

    /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
    fn check_non_exhaustive(
        &self,
        attr: &hir::Attribute,
        item: &hir::Item,
        target: Target,
    ) -> bool {
        match target {
            Target::Struct | Target::Enum => true,
            _ => {
                struct_span_err!(self.tcx.sess,
                                 attr.span,
                                 E0701,
                                 "attribute can only be applied to a struct or enum")
                    .span_label(item.span, "not a struct or enum")
                    .emit();
                false
            }
        }
    }

    /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
    fn check_marker(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) -> bool {
        match target {
            Target::Trait => true,
            _ => {
                self.tcx.sess
                    .struct_span_err(attr.span, "attribute can only be applied to a trait")
                    .span_label(item.span, "not a trait")
                    .emit();
                false
            }
        }
    }

    /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
    fn check_target_feature(
        &self,
        attr: &hir::Attribute,
        item: &hir::Item,
        target: Target,
    ) -> bool {
        match target {
            Target::Fn => true,
            _ => {
                self.tcx.sess
                    .struct_span_err(attr.span, "attribute should be applied to a function")
                    .span_label(item.span, "not a function")
                    .emit();
                false
            },
        }
    }

    /// Checks if the `#[repr]` attributes on `item` are valid.
    fn check_repr(&self, item: &hir::Item, target: Target) {
        // Extract the names of all repr hints, e.g., [foo, bar, align] for:
        // ```
        // #[repr(foo)]
        // #[repr(bar, align(8))]
        // ```
        let hints: Vec<_> = item.attrs
            .iter()
            .filter(|attr| attr.check_name(sym::repr))
            .filter_map(|attr| attr.meta_item_list())
            .flatten()
            .collect();

        let mut int_reprs = 0;
        let mut is_c = false;
        let mut is_simd = false;
        let mut is_transparent = false;

        for hint in &hints {
            let (article, allowed_targets) = match hint.name_or_empty() {
                name @ sym::C | name @ sym::align => {
                    is_c |= name == sym::C;
                    match target {
                        Target::Struct | Target::Union | Target::Enum => continue,
                        _ => ("a", "struct, enum, or union"),
                    }
                }
                sym::packed => {
                    if target != Target::Struct &&
                            target != Target::Union {
                                ("a", "struct or union")
                    } else {
                        continue
                    }
                }
                sym::simd => {
                    is_simd = true;
                    if target != Target::Struct {
                        ("a", "struct")
                    } else {
                        continue
                    }
                }
                sym::transparent => {
                    is_transparent = true;
                    match target {
                        Target::Struct | Target::Union | Target::Enum => continue,
                        _ => ("a", "struct, enum, or union"),
                    }
                }
                sym::i8  | sym::u8  | sym::i16 | sym::u16 |
                sym::i32 | sym::u32 | sym::i64 | sym::u64 |
                sym::isize | sym::usize => {
                    int_reprs += 1;
                    if target != Target::Enum {
                        ("an", "enum")
                    } else {
                        continue
                    }
                }
                _ => continue,
            };
            self.emit_repr_error(
                hint.span(),
                item.span,
                &format!("attribute should be applied to {}", allowed_targets),
                &format!("not {} {}", article, allowed_targets),
            )
        }

        // Just point at all repr hints if there are any incompatibilities.
        // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
        let hint_spans = hints.iter().map(|hint| hint.span());

        // Error on repr(transparent, <anything else>).
        if is_transparent && hints.len() > 1 {
            let hint_spans: Vec<_> = hint_spans.clone().collect();
            span_err!(self.tcx.sess, hint_spans, E0692,
                      "transparent {} cannot have other repr hints", target);
        }
        // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
        if (int_reprs > 1)
           || (is_simd && is_c)
           || (int_reprs == 1 && is_c && is_c_like_enum(item)) {
            let hint_spans: Vec<_> = hint_spans.collect();
            span_warn!(self.tcx.sess, hint_spans, E0566,
                       "conflicting representation hints");
        }
    }

    fn emit_repr_error(
        &self,
        hint_span: Span,
        label_span: Span,
        hint_message: &str,
        label_message: &str,
    ) {
        struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
            .span_label(label_span, label_message)
            .emit();
    }

    fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
        // When checking statements ignore expressions, they will be checked later
        if let hir::StmtKind::Local(ref l) = stmt.kind {
            for attr in l.attrs.iter() {
                if attr.check_name(sym::inline) {
                    self.check_inline(attr, &stmt.span, Target::Statement);
                }
                if attr.check_name(sym::repr) {
                    self.emit_repr_error(
                        attr.span,
                        stmt.span,
                        "attribute should not be applied to a statement",
                        "not a struct, enum, or union",
                    );
                }
            }
        }
    }

    fn check_expr_attributes(&self, expr: &hir::Expr) {
        let target = match expr.kind {
            hir::ExprKind::Closure(..) => Target::Closure,
            _ => Target::Expression,
        };
        for attr in expr.attrs.iter() {
            if attr.check_name(sym::inline) {
                self.check_inline(attr, &expr.span, target);
            }
            if attr.check_name(sym::repr) {
                self.emit_repr_error(
                    attr.span,
                    expr.span,
                    "attribute should not be applied to an expression",
                    "not defining a struct, enum, or union",
                );
            }
        }
    }

    fn check_used(&self, item: &hir::Item, target: Target) {
        for attr in &item.attrs {
            if attr.check_name(sym::used) && target != Target::Static {
                self.tcx.sess
                    .span_err(attr.span, "attribute must be applied to a `static` variable");
            }
        }
    }
}

impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
    }

    fn visit_item(&mut self, item: &'tcx hir::Item) {
        let target = Target::from_item(item);
        self.check_attributes(item, target);
        intravisit::walk_item(self, item)
    }

    fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
        self.check_stmt_attributes(stmt);
        intravisit::walk_stmt(self, stmt)
    }

    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        self.check_expr_attributes(expr);
        intravisit::walk_expr(self, expr)
    }
}

fn is_c_like_enum(item: &hir::Item) -> bool {
    if let hir::ItemKind::Enum(ref def, _) = item.kind {
        for variant in &def.variants {
            match variant.data {
                hir::VariantData::Unit(..) => { /* continue */ }
                _ => { return false; }
            }
        }
        true
    } else {
        false
    }
}

fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: DefId) {
    tcx.hir().visit_item_likes_in_module(
        module_def_id,
        &mut CheckAttrVisitor { tcx }.as_deep_visitor()
    );
}

pub(crate) fn provide(providers: &mut Providers<'_>) {
    *providers = Providers {
        check_mod_attrs,
        ..*providers
    };
}
<commit_msg>Refactor check_attr<commit_after>//! This module implements some validity checks for attributes.
//! In particular it verifies that `#[inline]` and `#[repr]` attributes are
//! attached to items that actually support them and if there are
//! conflicts between multiple such attributes attached to the same
//! item.

use crate::hir::{self, HirId, HirVec, Attribute, Item, ItemKind, TraitItem, TraitItemKind};
use crate::hir::DUMMY_HIR_ID;
use crate::hir::def_id::DefId;
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
use crate::ty::TyCtxt;
use crate::ty::query::Providers;

use std::fmt::{self, Display};
use syntax::{attr, symbol::sym};
use syntax_pos::Span;

#[derive(Copy, Clone, PartialEq)]
pub(crate) enum Target {
    ExternCrate,
    Use,
    Static,
    Const,
    Fn,
    Closure,
    Mod,
    ForeignMod,
    GlobalAsm,
    TyAlias,
    OpaqueTy,
    Enum,
    Struct,
    Union,
    Trait,
    TraitAlias,
    Impl,
    Expression,
    Statement,
}

impl Display for Target {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", match *self {
            Target::ExternCrate => "extern crate",
            Target::Use => "use",
            Target::Static => "static item",
            Target::Const => "constant item",
            Target::Fn => "function",
            Target::Closure => "closure",
            Target::Mod => "module",
            Target::ForeignMod => "foreign module",
            Target::GlobalAsm => "global asm",
            Target::TyAlias => "type alias",
            Target::OpaqueTy => "opaque type",
            Target::Enum => "enum",
            Target::Struct => "struct",
            Target::Union => "union",
            Target::Trait => "trait",
            Target::TraitAlias => "trait alias",
            Target::Impl => "item",
            Target::Expression => "expression",
            Target::Statement => "statement",
        })
    }
}

impl Target {
    pub(crate) fn from_item(item: &Item) -> Target {
        match item.kind {
            ItemKind::ExternCrate(..) => Target::ExternCrate,
            ItemKind::Use(..) => Target::Use,
            ItemKind::Static(..) => Target::Static,
            ItemKind::Const(..) => Target::Const,
            ItemKind::Fn(..) => Target::Fn,
            ItemKind::Mod(..) => Target::Mod,
            ItemKind::ForeignMod(..) => Target::ForeignMod,
            ItemKind::GlobalAsm(..) => Target::GlobalAsm,
            ItemKind::TyAlias(..) => Target::TyAlias,
            ItemKind::OpaqueTy(..) => Target::OpaqueTy,
            ItemKind::Enum(..) => Target::Enum,
            ItemKind::Struct(..) => Target::Struct,
            ItemKind::Union(..) => Target::Union,
            ItemKind::Trait(..) => Target::Trait,
            ItemKind::TraitAlias(..) => Target::TraitAlias,
            ItemKind::Impl(..) => Target::Impl,
        }
    }
        }
    }
}

struct CheckAttrVisitor<'tcx> {
    tcx: TyCtxt<'tcx>,
}

impl CheckAttrVisitor<'tcx> {
    /// Checks any attribute.
    fn check_attributes(
        &self,
        hir_id: HirId,
        attrs: &HirVec<Attribute>,
        span: &Span,
        target: Target,
        item: Option<&Item>,
    ) {
        let mut is_valid = true;
        for attr in attrs {
            is_valid &= if attr.check_name(sym::inline) {
                self.check_inline(hir_id, attr, span, target)
            } else if attr.check_name(sym::non_exhaustive) {
                self.check_non_exhaustive(attr, span, target)
            } else if attr.check_name(sym::marker) {
                self.check_marker(attr, span, target)
            } else if attr.check_name(sym::target_feature) {
                self.check_target_feature(attr, span, target)
            } else if attr.check_name(sym::track_caller) {
                self.check_track_caller(attr, &item, target)
            } else {
                true
            };
        }

        if !is_valid {
            return;
        }

        if target == Target::Fn {
            self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
        }

        self.check_repr(attrs, span, target, item);
        self.check_used(attrs, target);
    }

    /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
    fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
        match target {
            Target::Fn | Target::Closure | Target::Method { body: true } => true,
            _ => {
                struct_span_err!(self.tcx.sess,
                                 attr.span,
                                 E0518,
                                 "attribute should be applied to function or closure")
                    .span_label(*span, "not a function or closure")
                    .emit();
                false
            }
        }
    }

    /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
    fn check_track_caller(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) -> bool {
        if target != Target::Fn {
            struct_span_err!(
                self.tcx.sess,
                attr.span,
                E0739,
                "attribute should be applied to function"
            )
            .span_label(item.span, "not a function")
            .emit();
            false
        } else if attr::contains_name(&item.attrs, sym::naked) {
            struct_span_err!(
                self.tcx.sess,
                attr.span,
                E0736,
                "cannot use `#[track_caller]` with `#[naked]`",
            )
            .emit();
            false
        } else {
            true
        }
    }

    /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
    fn check_non_exhaustive(
        &self,
        attr: &Attribute,
        span: &Span,
        target: Target,
    ) -> bool {
        match target {
            Target::Struct | Target::Enum => true,
            _ => {
                struct_span_err!(self.tcx.sess,
                                 attr.span,
                                 E0701,
                                 "attribute can only be applied to a struct or enum")
                    .span_label(*span, "not a struct or enum")
                    .emit();
                false
            }
        }
    }

    /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
    fn check_marker(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
        match target {
            Target::Trait => true,
            _ => {
                self.tcx.sess
                    .struct_span_err(attr.span, "attribute can only be applied to a trait")
                    .span_label(*span, "not a trait")
                    .emit();
                false
            }
        }
    }

    /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
    fn check_target_feature(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
        match target {
            Target::Fn => true,
            _ => {
                self.tcx.sess
                    .struct_span_err(attr.span, "attribute should be applied to a function")
                    .span_label(*span, "not a function")
                    .emit();
                false
            },
        }
    }

    /// Checks if the `#[repr]` attributes on `item` are valid.
    fn check_repr(
        &self,
        attrs: &HirVec<Attribute>,
        span: &Span,
        target: Target,
        item: Option<&Item>,
    ) {
        // Extract the names of all repr hints, e.g., [foo, bar, align] for:
        // ```
        // #[repr(foo)]
        // #[repr(bar, align(8))]
        // ```
        let hints: Vec<_> = attrs
            .iter()
            .filter(|attr| attr.check_name(sym::repr))
            .filter_map(|attr| attr.meta_item_list())
            .flatten()
            .collect();

        let mut int_reprs = 0;
        let mut is_c = false;
        let mut is_simd = false;
        let mut is_transparent = false;

        for hint in &hints {
            let (article, allowed_targets) = match hint.name_or_empty() {
                name @ sym::C | name @ sym::align => {
                    is_c |= name == sym::C;
                    match target {
                        Target::Struct | Target::Union | Target::Enum => continue,
                        _ => ("a", "struct, enum, or union"),
                    }
                }
                sym::packed => {
                    if target != Target::Struct &&
                            target != Target::Union {
                                ("a", "struct or union")
                    } else {
                        continue
                    }
                }
                sym::simd => {
                    is_simd = true;
                    if target != Target::Struct {
                        ("a", "struct")
                    } else {
                        continue
                    }
                }
                sym::transparent => {
                    is_transparent = true;
                    match target {
                        Target::Struct | Target::Union | Target::Enum => continue,
                        _ => ("a", "struct, enum, or union"),
                    }
                }
                sym::i8  | sym::u8  | sym::i16 | sym::u16 |
                sym::i32 | sym::u32 | sym::i64 | sym::u64 |
                sym::isize | sym::usize => {
                    int_reprs += 1;
                    if target != Target::Enum {
                        ("an", "enum")
                    } else {
                        continue
                    }
                }
                _ => continue,
            };
            self.emit_repr_error(
                hint.span(),
                *span,
                &format!("attribute should be applied to {}", allowed_targets),
                &format!("not {} {}", article, allowed_targets),
            )
        }

        // Just point at all repr hints if there are any incompatibilities.
        // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
        let hint_spans = hints.iter().map(|hint| hint.span());

        // Error on repr(transparent, <anything else>).
        if is_transparent && hints.len() > 1 {
            let hint_spans: Vec<_> = hint_spans.clone().collect();
            span_err!(self.tcx.sess, hint_spans, E0692,
                      "transparent {} cannot have other repr hints", target);
        }
        // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
        if (int_reprs > 1)
           || (is_simd && is_c)
           || (int_reprs == 1 && is_c && item.map(|item| is_c_like_enum(item)).unwrap_or(false)) {
            let hint_spans: Vec<_> = hint_spans.collect();
            span_warn!(self.tcx.sess, hint_spans, E0566,
                       "conflicting representation hints");
        }
    }

    fn emit_repr_error(
        &self,
        hint_span: Span,
        label_span: Span,
        hint_message: &str,
        label_message: &str,
    ) {
        struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
            .span_label(label_span, label_message)
            .emit();
    }

    fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
        // When checking statements ignore expressions, they will be checked later
        if let hir::StmtKind::Local(ref l) = stmt.kind {
            for attr in l.attrs.iter() {
                if attr.check_name(sym::inline) {
                    self.check_inline(DUMMY_HIR_ID, attr, &stmt.span, Target::Statement);
                }
                if attr.check_name(sym::repr) {
                    self.emit_repr_error(
                        attr.span,
                        stmt.span,
                        "attribute should not be applied to a statement",
                        "not a struct, enum, or union",
                    );
                }
            }
        }
    }

    fn check_expr_attributes(&self, expr: &hir::Expr) {
        let target = match expr.kind {
            hir::ExprKind::Closure(..) => Target::Closure,
            _ => Target::Expression,
        };
        for attr in expr.attrs.iter() {
            if attr.check_name(sym::inline) {
                self.check_inline(DUMMY_HIR_ID, attr, &expr.span, target);
            }
            if attr.check_name(sym::repr) {
                self.emit_repr_error(
                    attr.span,
                    expr.span,
                    "attribute should not be applied to an expression",
                    "not defining a struct, enum, or union",
                );
            }
        }
    }

    fn check_used(&self, attrs: &HirVec<Attribute>, target: Target) {
        for attr in attrs {
            if attr.check_name(sym::used) && target != Target::Static {
                self.tcx.sess
                    .span_err(attr.span, "attribute must be applied to a `static` variable");
            }
        }
    }
}

impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
    }

    fn visit_item(&mut self, item: &'tcx Item) {
        let target = Target::from_item(item);
        self.check_attributes(item.hir_id, &item.attrs, &item.span, target, Some(item));
        intravisit::walk_item(self, item)
    }

    fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
        self.check_stmt_attributes(stmt);
        intravisit::walk_stmt(self, stmt)
    }

    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        self.check_expr_attributes(expr);
        intravisit::walk_expr(self, expr)
    }
}

fn is_c_like_enum(item: &Item) -> bool {
    if let ItemKind::Enum(ref def, _) = item.kind {
        for variant in &def.variants {
            match variant.data {
                hir::VariantData::Unit(..) => { /* continue */ }
                _ => return false,
            }
        }
        true
    } else {
        false
    }
}

fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: DefId) {
    tcx.hir().visit_item_likes_in_module(
        module_def_id,
        &mut CheckAttrVisitor { tcx }.as_deep_visitor()
    );
}

pub(crate) fn provide(providers: &mut Providers<'_>) {
    *providers = Providers {
        check_mod_attrs,
        ..*providers
    };
}
<|endoftext|>


<commit_before>#![feature(core)]

extern crate core;

use std::Box;
use std::{io, rand};
use core::ptr;

macro_rules! readln {
    () => {
        {
            let mut line = String::new();
            match io::stdin().read_line(&mut line) {
                Ok(_) => Some(line.trim().to_string()),
                Err(_) => None
            }
        }
    };
}

fn console_title(title: &str){

}

#[no_mangle]
pub fn main() {
    console_title("Test");

    println!("Type help for a command list");
    while let Some(line) = readln!() {
        let args: Vec<String> = line.split(' ').map(|arg| arg.to_string()).collect();

        if let Some(a_command) = args.get(0) {
            println!("# {}", line);
            let console_commands = ["panic",
                                    "ls",
                                    "ptr_write",
                                    "box_write",];

            match &a_command[..] {
                command if command == console_commands[0] =>
                    panic!("Test panic"),
                command if command == console_commands[1] => {
                    // TODO: import std::fs functions into libredox
                    //fs::read_dir("/").unwrap().map(|dir| println!("{}", dir));
                }
                command if command == console_commands[2] => {
                    let a_ptr = rand() as *mut u8;
                    unsafe { ptr::write(a_ptr, rand() as u8); }
                }
                command if command == console_commands[3] => {
                    let mut a_box = Box::new(rand() as u8);
                    unsafe { ptr::write(Box::into_raw(a_box), rand() as u8); }
                }
                _ => println!("Commands: {}", console_commands.join(" ")),
            }
        }
    }
}
<commit_msg>Add more tests to test.rs<commit_after>#![feature(asm)]

use std::Box;
use std::{io, rand};
use std::ptr;

macro_rules! readln {
    () => {
        {
            let mut line = String::new();
            match io::stdin().read_line(&mut line) {
                Ok(_) => Some(line.trim().to_string()),
                Err(_) => None
            }
        }
    };
}

fn console_title(title: &str){

}

#[no_mangle]
pub fn main() {
    console_title("Test");

    println!("Type help for a command list");
    while let Some(line) = readln!() {
        let args: Vec<String> = line.split(' ').map(|arg| arg.to_string()).collect();

        if let Some(a_command) = args.get(0) {
            println!("# {}", line);
            let console_commands = ["panic",
                                    "ls",
                                    "ptr_write",
                                    "box_write",
                                    "reboot",
                                    "shutdown"];

            match &a_command[..] {
                command if command == console_commands[0] =>
                    panic!("Test panic"),
                command if command == console_commands[1] => {
                    // TODO: import std::fs functions into libredox
                    //fs::read_dir("/").unwrap().map(|dir| println!("{}", dir));
                }
                command if command == console_commands[2] => {
                    let a_ptr = rand() as *mut u8;
                    unsafe { ptr::write(a_ptr, rand() as u8); }
                }
                command if command == console_commands[3] => {
                    let mut a_box = Box::new(rand() as u8);
                    unsafe { ptr::write(Box::into_raw(a_box), rand() as u8); }
                }
                command if command == console_commands[4] => {
                    unsafe {
                        let mut good: u8 = 2;
                        while good & 2 == 2 {
                            asm!("in al, dx" : "={al}"(good) : "{dx}"(0x64) : : "intel", "volatile");
                        }
                        asm!("out dx, al" : : "{dx}"(0x64), "{al}"(0xFE) : : "intel", "volatile");
                        loop {
                            asm!("cli" : : : : "intel", "volatile");
                            asm!("hlt" : : : : "intel", "volatile");
                        }
                    }
                }
                command if command == console_commands[5] => {
                    unsafe {
                        loop {
                            asm!("cli" : : : : "intel", "volatile");
                            asm!("hlt" : : : : "intel", "volatile");
                        }
                    }
                }
                _ => println!("Commands: {}", console_commands.join(" ")),
            }
        }
    }
}
<|endoftext|>


<commit_before>/*
    Copyright 2013 Jesse 'Jeaye' Wilkerson
    See licensing in LICENSE file, or at:
        http://www.opensource.org/licenses/BSD-3-Clause

    File: gl/texture.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      A wrapper of arbitrary OpenGL textures.
*/

extern mod std;
extern mod opengles;
extern mod stb_image;
use gl = opengles::gl2;

mod util;

#[macro_escape]
mod check_internal;

struct Texture
{
  target: gl::GLenum,
  obj: gl::GLuint,
  filename: @str,
}

impl Texture
{
  #[inline(always)]
  pub fn new(targ: gl::GLenum, file: &str) -> Texture
  {
    let mut tex = Texture
    {
      target: targ,
      obj: check!(gl::gen_textures(1))[0],
      filename: file.to_managed(),
    };

    check!(gl::bind_texture(tex.target, tex.obj));

    match stb_image::image::load(file.to_owned())
    {
      stb_image::image::ImageU8(image) =>
      {
        debug!(fmt!("Loaded image %s with %?x%?:%?",
                    tex.filename, image.width, image.height, image.depth));
        unsafe {
          check!(gl::tex_image_2d
          (
            gl::TEXTURE_2D, 0,
            gl::RGB as gl::GLint,
            image.width as gl::GLsizei,
            image.height as gl::GLsizei,
            0, gl::RGB, gl::UNSIGNED_BYTE,
            Some(cast::transmute((copy image.data, image.data.len())))
            //(cast::transmute(&image.data[0]))
          ));
        }
      }
      _ => fail!(fmt!("Failed to load texture %s", tex.filename))
    }

    tex
  }

  #[inline(always)]
  pub fn bind(&self, unit: gl::GLenum)
  {
    //check!(gl::active_texture(unit));
    check!(gl::bind_texture(gl::TEXTURE0 + unit, self.obj));
  }
}

<commit_msg> #13 Small optimization for texture loading<commit_after>/*
    Copyright 2013 Jesse 'Jeaye' Wilkerson
    See licensing in LICENSE file, or at:
        http://www.opensource.org/licenses/BSD-3-Clause

    File: gl/texture.rs
    Author: Jesse 'Jeaye' Wilkerson
    Description:
      A wrapper of arbitrary OpenGL textures.
*/

extern mod std;
extern mod opengles;
extern mod stb_image;
use gl = opengles::gl2;

mod util;

#[macro_escape]
mod check_internal;

struct Texture
{
  target: gl::GLenum,
  obj: gl::GLuint,
  filename: @str,
}

impl Texture
{
  #[inline(always)]
  pub fn new(targ: gl::GLenum, file: &str) -> Texture
  {
    let mut tex = Texture
    {
      target: targ,
      obj: check!(gl::gen_textures(1))[0],
      filename: file.to_managed(),
    };

    check!(gl::bind_texture(tex.target, tex.obj));

    match stb_image::image::load(file.to_owned())
    {
      stb_image::image::ImageU8(ref image) =>
      {
        debug!(fmt!("Loaded image %s with %?x%?:%?",
                    tex.filename, image.width, image.height, image.depth));
        unsafe {
          check!(gl::tex_image_2d
          (
            gl::TEXTURE_2D, 0,
            gl::RGB as gl::GLint,
            image.width as gl::GLsizei,
            image.height as gl::GLsizei,
            0, gl::RGB, gl::UNSIGNED_BYTE,
            Some(cast::transmute((copy image.data, image.data.len())))
            //(cast::transmute(&image.data[0]))
          ));
        }
      }
      _ => fail!(fmt!("Failed to load texture %s", tex.filename))
    }

    tex
  }

  #[inline(always)]
  pub fn bind(&self, unit: gl::GLenum)
  {
    //check!(gl::active_texture(unit));
    check!(gl::bind_texture(gl::TEXTURE0 + unit, self.obj));
  }
}

<|endoftext|>


<commit_before>use std::collections::HashSet;
use botocore::{Service, ShapeType};
use super::mutate_type_name;

/*
pub fn find_output_types(service: &Service) -> HashSet<String> {
    let mut output_types = HashSet::new();

    for operation in service.operations.values() {
        if let Some(ref output) = operation.output {
            output_types.insert(output.shape.to_owned());
        }
    }
    output_types
}
*/
pub fn filter_types(service: &Service) -> (HashSet<String>, HashSet<String>) {
    let mut deserialized_types: HashSet<String> = HashSet::new();
    let mut serialized_types: HashSet<String> = HashSet::new();
    for operation in service.operations.values() {
        if let Some(ref output) = operation.output {

            let output_shape = service.shapes.get(&output.shape).expect("Shape type missing from service definition");

            // output shapes with a payload blob don't get deserialized
            let streaming = match output_shape.payload {
                None => false,
                Some(ref payload_member) => {
                    let payload_shape = &service.shapes[payload_member];
                    payload_shape.shape_type == ShapeType::Blob || payload_shape.shape_type == ShapeType::String
                }
            };

            if !streaming {
                recurse_find_shapes(service, &mut deserialized_types, &output.shape);
            }
        }
        if let Some(ref input) = operation.input {
            recurse_find_shapes(service, &mut serialized_types, &input.shape);
        }
    }

    println!("----------------------\n{:#?}\n----------------------------\n", deserialized_types);

    (serialized_types, deserialized_types)
}

fn recurse_find_shapes(service: &Service, types: &mut HashSet<String>, shape_name: &str) {
    types.insert(mutate_type_name(shape_name).to_owned());
    let shape = service.shapes.get(shape_name).expect("Shape type missing from service definition");
    match shape.shape_type {
        ShapeType::Structure => {
            if let Some(ref members) = shape.members {
                for member in members.values() {
                    if Some(true) != member.deprecated &&
                       member.location != Some("header".to_owned()) &&
                       member.location != Some("headers".to_owned()) &&
                       !types.contains(&member.shape) {
                        println!("{} -> {}", mutate_type_name(shape_name), member.shape);
                        recurse_find_shapes(service, types, &member.shape);
                    }
                }
            }
        }
        ShapeType::Map => {
            println!("{} -> {}", mutate_type_name(shape_name), mutate_type_name(shape.key_type()));
            recurse_find_shapes(service, types, shape.key_type());
            println!("{} -> {}", mutate_type_name(shape_name), mutate_type_name(shape.value_type()));
            recurse_find_shapes(service, types, shape.value_type());
        }
        ShapeType::List => {
            println!("{} -> {}", mutate_type_name(shape_name), mutate_type_name(shape.member_type()));
            recurse_find_shapes(service, types, shape.member_type());
        }
        _ => {}
    }
}
<commit_msg>remove noisy debug println again<commit_after>use std::collections::HashSet;
use botocore::{Service, ShapeType};
use super::mutate_type_name;

/*
pub fn find_output_types(service: &Service) -> HashSet<String> {
    let mut output_types = HashSet::new();

    for operation in service.operations.values() {
        if let Some(ref output) = operation.output {
            output_types.insert(output.shape.to_owned());
        }
    }
    output_types
}
*/
pub fn filter_types(service: &Service) -> (HashSet<String>, HashSet<String>) {
    let mut deserialized_types: HashSet<String> = HashSet::new();
    let mut serialized_types: HashSet<String> = HashSet::new();
    for operation in service.operations.values() {
        if let Some(ref output) = operation.output {

            let output_shape = service.shapes.get(&output.shape).expect("Shape type missing from service definition");

            // output shapes with a payload blob don't get deserialized
            let streaming = match output_shape.payload {
                None => false,
                Some(ref payload_member) => {
                    let payload_shape = &service.shapes[payload_member];
                    payload_shape.shape_type == ShapeType::Blob || payload_shape.shape_type == ShapeType::String
                }
            };

            if !streaming {
                recurse_find_shapes(service, &mut deserialized_types, &output.shape);
            }
        }
        if let Some(ref input) = operation.input {
            recurse_find_shapes(service, &mut serialized_types, &input.shape);
        }
    }

    (serialized_types, deserialized_types)
}

fn recurse_find_shapes(service: &Service, types: &mut HashSet<String>, shape_name: &str) {
    types.insert(mutate_type_name(shape_name).to_owned());
    let shape = service.shapes.get(shape_name).expect("Shape type missing from service definition");
    match shape.shape_type {
        ShapeType::Structure => {
            if let Some(ref members) = shape.members {
                for member in members.values() {
                    if Some(true) != member.deprecated &&
                       member.location != Some("header".to_owned()) &&
                       member.location != Some("headers".to_owned()) &&
                       !types.contains(&member.shape) {
                        recurse_find_shapes(service, types, &member.shape);
                    }
                }
            }
        }
        ShapeType::Map => {
            recurse_find_shapes(service, types, shape.key_type());
            recurse_find_shapes(service, types, shape.value_type());
        }
        ShapeType::List => {
            recurse_find_shapes(service, types, shape.member_type());
        }
        _ => {}
    }
}
<|endoftext|>


<commit_before>use pango::{self, LayoutExt, Layout};
use cairo_sys;
use pangocairo::CairoContextExt;
use bar_properties::BarProperties;
use xcb::{self, Window, Screen, Visualtype, Pixmap, Connection};
use cairo::{Context, Surface};
use std::rc::Rc;
use error::Result;

pub struct ItemState {
    bar_props: Rc<BarProperties>,
    conn: Rc<Connection>,
    content_width: u16,
    id: usize,
    pixmap: Pixmap,
    screen_number: usize,
    state: String,
    surface: Option<Surface>,
    surface_width: u16,
    visualtype: Visualtype,
    window: Window,
}

impl ItemState {
    pub fn new(
        id: usize,
        bar_props: Rc<BarProperties>,
        screen_number: usize,
        visualtype: Visualtype,
        conn: Rc<Connection>,
        window: Window,
    ) -> ItemState {
        let pixmap = conn.generate_id();
        ItemState {
            bar_props,
            conn,
            content_width: 1,
            id,
            pixmap,
            screen_number: screen_number,
            state: String::new(),
            surface: None,
            surface_width: 0,
            visualtype,
            window,
        }
    }

    #[inline]
    pub fn is_ready(&self) -> bool {
        self.surface.is_some()
    }

    #[inline]
    pub fn get_content_width(&self) -> u16 {
        self.content_width
    }

    #[inline]
    pub fn get_pixmap(&self) -> Pixmap {
        self.pixmap
    }

    #[inline]
    pub fn get_id(&self) -> usize {
        self.id
    }

    #[inline]
    fn get_screen<'s>(&'s self) -> Screen<'s> {
        let setup = self.conn.get_setup();
        setup.roots().nth(self.screen_number).unwrap()
    }

    fn update_surface(&mut self) -> Result<()> {
        let width = self.content_width;

        if width > self.surface_width || width < (self.surface_width / 10) * 6 ||
            self.surface.is_none()
        {
            self.surface_width = (width / 10) * 13 + 1;

            xcb::free_pixmap(&self.conn, self.pixmap);
            self.pixmap = self.conn.generate_id();

            try_xcb!(
                xcb::create_pixmap_checked,
                "failed to create pixmap",
                &self.conn,
                self.get_screen().root_depth(),
                self.pixmap,
                self.window,
                self.surface_width as u16,
                self.bar_props.area.height()
            );

            self.surface = Some(unsafe {
                Surface::from_raw_full(cairo_sys::cairo_xcb_surface_create(
                    (self.conn.get_raw_conn() as *mut cairo_sys::xcb_connection_t),
                    //self.get_screen().ptr as *mut cairo_sys::xcb_screen_t,
                    self.pixmap,
                    (&mut self.visualtype.base as *mut xcb::ffi::xcb_visualtype_t) as
                        *mut cairo_sys::xcb_visualtype_t,
                    self.surface_width as i32,
                    self.bar_props.area.height() as i32,
                ))
            });
        }
        Ok(())
    }

    fn create_layout(&self, ctx: &Context) -> Layout {
        let layout = ctx.create_pango_layout();

        layout.set_font_description(Some(&self.bar_props.font));
        layout.set_text(self.state.as_str(), self.state.len() as i32);
        ctx.update_pango_layout(&layout);

        layout
    }

    pub fn update_size(&mut self) -> Result<()> {
        self.update_surface()?;
        let surface = match self.surface {
            Some(ref surface) => surface,
            None => return Err("no surface".into()),
        };

        let ctx = Context::new(&surface);
        let layout = self.create_layout(&ctx);

        self.content_width = layout.get_pixel_size().0 as u16;

        Ok(())
    }

    pub fn update(&mut self, update: String) -> Result<()> {
        if update != self.state {
            self.state = update;
            self.update_size()?;
            self.paint()?;
        }
        Ok(())
    }

    pub fn paint(&mut self) -> Result<()> {
        self.update_surface()?;
        let surface = match self.surface {
            Some(ref surface) => surface,
            None => return Err("no surface".into()),
        };

        let ctx = Context::new(&surface);
        let layout = self.create_layout(&ctx);

        ctx.set_source_rgb(
            self.bar_props.bg_color.red,
            self.bar_props.bg_color.green,
            self.bar_props.bg_color.blue,
        );
        ctx.paint();
        ctx.set_source_rgb(
            self.bar_props.fg_color.red,
            self.bar_props.fg_color.green,
            self.bar_props.fg_color.blue,
        );
        ctx.set_source_rgb(0.1, 0.1, 0.1);

        let text_height = self.bar_props.font.get_size() as f64 / pango::SCALE as f64;
        let baseline = self.bar_props.area.height() as f64 / 2. + (text_height / 2.) -
            (layout.get_baseline() as f64 / pango::SCALE as f64);

        ctx.move_to(0., baseline.floor() - 1.);
        ctx.update_pango_layout(&layout);
        ctx.show_pango_layout(&layout);

        self.conn.flush();

        Ok(())
    }
}
<commit_msg>Add support for pango markup in Components.<commit_after>use pango::{self, LayoutExt, Layout};
use cairo_sys;
use pangocairo::CairoContextExt;
use bar_properties::BarProperties;
use xcb::{self, Window, Screen, Visualtype, Pixmap, Connection};
use cairo::{Context, Surface};
use std::rc::Rc;
use error::Result;

pub struct ItemState {
    bar_props: Rc<BarProperties>,
    conn: Rc<Connection>,
    content_width: u16,
    id: usize,
    pixmap: Pixmap,
    screen_number: usize,
    state: String,
    surface: Option<Surface>,
    surface_width: u16,
    visualtype: Visualtype,
    window: Window,
}

impl ItemState {
    pub fn new(
        id: usize,
        bar_props: Rc<BarProperties>,
        screen_number: usize,
        visualtype: Visualtype,
        conn: Rc<Connection>,
        window: Window,
    ) -> ItemState {
        let pixmap = conn.generate_id();
        ItemState {
            bar_props,
            conn,
            content_width: 1,
            id,
            pixmap,
            screen_number: screen_number,
            state: String::new(),
            surface: None,
            surface_width: 0,
            visualtype,
            window,
        }
    }

    #[inline]
    pub fn is_ready(&self) -> bool {
        self.surface.is_some()
    }

    #[inline]
    pub fn get_content_width(&self) -> u16 {
        self.content_width
    }

    #[inline]
    pub fn get_pixmap(&self) -> Pixmap {
        self.pixmap
    }

    #[inline]
    pub fn get_id(&self) -> usize {
        self.id
    }

    #[inline]
    fn get_screen<'s>(&'s self) -> Screen<'s> {
        let setup = self.conn.get_setup();
        setup.roots().nth(self.screen_number).unwrap()
    }

    fn update_surface(&mut self) -> Result<()> {
        let width = self.content_width;

        if width > self.surface_width || width < (self.surface_width / 10) * 6 ||
            self.surface.is_none()
        {
            self.surface_width = (width / 10) * 13 + 1;

            xcb::free_pixmap(&self.conn, self.pixmap);
            self.pixmap = self.conn.generate_id();

            try_xcb!(
                xcb::create_pixmap_checked,
                "failed to create pixmap",
                &self.conn,
                self.get_screen().root_depth(),
                self.pixmap,
                self.window,
                self.surface_width as u16,
                self.bar_props.area.height()
            );

            self.surface = Some(unsafe {
                Surface::from_raw_full(cairo_sys::cairo_xcb_surface_create(
                    (self.conn.get_raw_conn() as *mut cairo_sys::xcb_connection_t),
                    //self.get_screen().ptr as *mut cairo_sys::xcb_screen_t,
                    self.pixmap,
                    (&mut self.visualtype.base as *mut xcb::ffi::xcb_visualtype_t) as
                        *mut cairo_sys::xcb_visualtype_t,
                    self.surface_width as i32,
                    self.bar_props.area.height() as i32,
                ))
            });
        }
        Ok(())
    }

    fn create_layout(&self, ctx: &Context) -> Layout {
        let layout = ctx.create_pango_layout();

        layout.set_font_description(Some(&self.bar_props.font));
        layout.set_markup(self.state.as_str(), self.state.len() as i32);
        ctx.update_pango_layout(&layout);

        layout
    }

    pub fn update_size(&mut self) -> Result<()> {
        self.update_surface()?;
        let surface = match self.surface {
            Some(ref surface) => surface,
            None => return Err("no surface".into()),
        };

        let ctx = Context::new(&surface);
        let layout = self.create_layout(&ctx);

        self.content_width = layout.get_pixel_size().0 as u16;

        Ok(())
    }

    pub fn update(&mut self, update: String) -> Result<()> {
        if update != self.state {
            self.state = update;
            self.update_size()?;
            self.paint()?;
        }
        Ok(())
    }

    pub fn paint(&mut self) -> Result<()> {
        self.update_surface()?;
        let surface = match self.surface {
            Some(ref surface) => surface,
            None => return Err("no surface".into()),
        };

        let ctx = Context::new(&surface);
        let layout = self.create_layout(&ctx);

        ctx.set_source_rgb(
            self.bar_props.bg_color.red,
            self.bar_props.bg_color.green,
            self.bar_props.bg_color.blue,
        );
        ctx.paint();
        ctx.set_source_rgb(
            self.bar_props.fg_color.red,
            self.bar_props.fg_color.green,
            self.bar_props.fg_color.blue,
        );
        ctx.set_source_rgb(0.1, 0.1, 0.1);

        let text_height = self.bar_props.font.get_size() as f64 / pango::SCALE as f64;
        let baseline = self.bar_props.area.height() as f64 / 2. + (text_height / 2.) -
            (layout.get_baseline() as f64 / pango::SCALE as f64);

        ctx.move_to(0., baseline.floor() - 1.);
        ctx.update_pango_layout(&layout);
        ctx.show_pango_layout(&layout);

        self.conn.flush();

        Ok(())
    }
}
<|endoftext|>