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

//! This file defines transaction store APIs that are related to committed signed transactions.

use crate::{
    change_set::ChangeSet,
    errors::AptosDbError,
    schema::{
        transaction::TransactionSchema, transaction_by_account::TransactionByAccountSchema,
        transaction_by_hash::TransactionByHashSchema, write_set::WriteSetSchema,
    },
    transaction_accumulator::TransactionAccumulatorSchema,
    transaction_info::TransactionInfoSchema,
};
use anyhow::{ensure, format_err, Result};
use aptos_crypto::{hash::CryptoHash, HashValue};
use aptos_types::{
    account_address::AccountAddress,
    block_metadata::BlockMetadata,
    proof::position::Position,
    transaction::{Transaction, Version},
    write_set::WriteSet,
};
use schemadb::{ReadOptions, SchemaBatch, SchemaIterator, DB};
use std::sync::Arc;

#[derive(Clone, Debug)]
pub struct TransactionStore {
    db: Arc<DB>,
}

impl TransactionStore {
    pub fn new(db: Arc<DB>) -> Self {
        Self { db }
    }

    /// Gets the version of a transaction by the sender `address` and `sequence_number`.
    pub fn get_account_transaction_version(
        &self,
        address: AccountAddress,
        sequence_number: u64,
        ledger_version: Version,
    ) -> Result<Option<Version>> {
        if let Some(version) = self
            .db
            .get::<TransactionByAccountSchema>(&(address, sequence_number))?
        {
            if version <= ledger_version {
                return Ok(Some(version));
            }
        }

        Ok(None)
    }

    /// Gets the version of a transaction by its hash.
    pub fn get_transaction_version_by_hash(
        &self,
        hash: &HashValue,
        ledger_version: Version,
    ) -> Result<Option<Version>> {
        Ok(match self.db.get::<TransactionByHashSchema>(hash)? {
            Some(version) if version <= ledger_version => Some(version),
            _ => None,
        })
    }

    /// Gets an iterator that yields `(sequence_number, version)` for each
    /// transaction sent by an account, starting at `start_seq_num`, and returning
    /// at most `num_versions` results with `version <= ledger_version`.
    ///
    /// Guarantees that the returned sequence numbers are sequential, i.e.,
    /// `seq_num_{i} + 1 = seq_num_{i+1}`.
    pub fn get_account_transaction_version_iter(
        &self,
        address: AccountAddress,
        start_seq_num: u64,
        num_versions: u64,
        ledger_version: Version,
    ) -> Result<AccountTransactionVersionIter> {
        let mut iter = self
            .db
            .iter::<TransactionByAccountSchema>(ReadOptions::default())?;
        iter.seek(&(address, start_seq_num))?;
        Ok(AccountTransactionVersionIter {
            inner: iter,
            address,
            expected_next_seq_num: start_seq_num,
            end_seq_num: start_seq_num
                .checked_add(num_versions)
                .ok_or_else(|| format_err!("too many transactions requested"))?,
            prev_version: None,
            ledger_version,
        })
    }

    /// Get signed transaction given `version`
    pub fn get_transaction(&self, version: Version) -> Result<Transaction> {
        self.db
            .get::<TransactionSchema>(&version)?
            .ok_or_else(|| AptosDbError::NotFound(format!("Txn {}", version)).into())
    }

    /// Gets an iterator that yields `num_transactions` transactions starting from `start_version`.
    pub fn get_transaction_iter(
        &self,
        start_version: Version,
        num_transactions: usize,
    ) -> Result<TransactionIter> {
        let mut iter = self.db.iter::<TransactionSchema>(ReadOptions::default())?;
        iter.seek(&start_version)?;
        Ok(TransactionIter {
            inner: iter,
            expected_next_version: start_version,
            end_version: start_version
                .checked_add(num_transactions as u64)
                .ok_or_else(|| format_err!("too many transactions requested"))?,
        })
    }

    /// Get the first version that txn starts existent.
    pub fn get_first_txn_version(&self) -> Result<Option<Version>> {
        let mut iter = self.db.iter::<TransactionSchema>(Default::default())?;
        iter.seek_to_first();
        iter.next().map(|res| res.map(|(v, _)| v)).transpose()
    }

    /// Returns the block metadata carried on the block metadata transaction at or preceding
    /// `version`, together with the version of the block metadata transaction.
    /// Returns None if there's no such transaction at or preceding `version` (it's likely the genesis
    /// version 0).
    pub fn get_block_metadata(&self, version: Version) -> Result<Option<(Version, BlockMetadata)>> {
        // Must be larger than a block size, otherwise a NotFound error will be raised wrongly.
        const MAX_VERSIONS_TO_SEARCH: usize = 1000 * 100;

        // Linear search via `DB::rev_iter()` here, NOT expecting performance hit, due to the fact
        // that the iterator caches data block and that there are limited number of transactions in
        // each block.
        let mut iter = self.db.rev_iter::<TransactionSchema>(Default::default())?;
        iter.seek(&version)?;
        for res in iter.take(MAX_VERSIONS_TO_SEARCH) {
            let (v, txn) = res?;
            if let Transaction::BlockMetadata(block_meta) = txn {
                return Ok(Some((v, block_meta)));
            } else if v == 0 {
                return Ok(None);
            }
        }

        Err(AptosDbError::NotFound(format!("BlockMetadata preceding version {}", version)).into())
    }

    /// Save signed transaction at `version`
    pub fn put_transaction(
        &self,
        version: Version,
        transaction: &Transaction,
        cs: &mut ChangeSet,
    ) -> Result<()> {
        if let Transaction::UserTransaction(txn) = transaction {
            cs.batch.put::<TransactionByAccountSchema>(
                &(txn.sender(), txn.sequence_number()),
                &version,
            )?;
        }
        cs.batch
            .put::<TransactionByHashSchema>(&transaction.hash(), &version)?;
        cs.batch.put::<TransactionSchema>(&version, transaction)?;

        Ok(())
    }

    /// Get executed transaction vm output given `version`
    pub fn get_write_set(&self, version: Version) -> Result<WriteSet> {
        self.db.get::<WriteSetSchema>(&version)?.ok_or_else(|| {
            AptosDbError::NotFound(format!("WriteSet at version {}", version)).into()
        })
    }

    /// Get write sets in `[begin_version, end_version)` half-open range.
    ///
    /// N.b. an empty `Vec` is returned when `begin_version == end_version`
    pub fn get_write_sets(
        &self,
        begin_version: Version,
        end_version: Version,
    ) -> Result<Vec<WriteSet>> {
        if begin_version == end_version {
            return Ok(Vec::new());
        }
        ensure!(
            begin_version < end_version,
            "begin_version {} >= end_version {}",
            begin_version,
            end_version
        );

        let mut iter = self.db.iter::<WriteSetSchema>(Default::default())?;
        iter.seek(&begin_version)?;

        let mut ret = Vec::with_capacity((end_version - begin_version) as usize);
        for current_version in begin_version..end_version {
            let (version, write_set) = iter
                .next()
                .transpose()?
                .ok_or_else(|| format_err!("Write set missing for version {}", current_version))?;
            ensure!(
                version == current_version,
                "Write set missing for version {}, got version {}",
                current_version,
                version,
            );
            ret.push(write_set);
        }

        Ok(ret)
    }

    /// Get the first version that write set starts existent.
    pub fn get_first_write_set_version(&self) -> Result<Option<Version>> {
        let mut iter = self.db.iter::<WriteSetSchema>(Default::default())?;
        iter.seek_to_first();
        iter.next().map(|res| res.map(|(v, _)| v)).transpose()
    }

    /// Save executed transaction vm output given `version`
    pub fn put_write_set(
        &self,
        version: Version,
        write_set: &WriteSet,
        cs: &mut ChangeSet,
    ) -> Result<()> {
        cs.batch.put::<WriteSetSchema>(&version, write_set)
    }

    /// Prune the transaction by hash store given a list of transaction
    pub fn prune_transaction_by_hash(
        &self,
        transactions: &[Transaction],
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for transaction in transactions {
            db_batch.delete::<TransactionByHashSchema>(&transaction.hash())?;
        }
        Ok(())
    }

    /// Prune the transaction by account store given a list of transaction
    pub fn prune_transaction_by_account(
        &self,
        transactions: &[Transaction],
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for transaction in transactions {
            if let Transaction::UserTransaction(txn) = transaction {
                db_batch
                    .delete::<TransactionByAccountSchema>(&(txn.sender(), txn.sequence_number()))?;
            }
        }
        Ok(())
    }

    /// Prune the transaction schema store between a range of version in [begin, end)
    pub fn prune_transaction_schema(
        &self,
        begin: Version,
        end: Version,
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for version in begin..end {
            db_batch.delete::<TransactionSchema>(&version)?;
        }
        Ok(())
    }

    /// Prune the transaction schema store between a range of version in [begin, end)
    pub fn prune_transaction_info_schema(
        &self,
        begin: Version,
        end: Version,
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for version in begin..end {
            db_batch.delete::<TransactionInfoSchema>(&version)?;
        }
        Ok(())
    }

    /// Prune the transaction schema store between a range of version in [begin, end)
    pub fn prune_write_set(
        &self,
        begin: Version,
        end: Version,
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for version in begin..end {
            db_batch.delete::<WriteSetSchema>(&version)?;
        }
        Ok(())
    }

    /// Prune the transaction schema store between a range of version in [begin, end).
    pub fn prune_transaction_accumulator(
        &self,
        begin: Version,
        end: Version,
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        let begin_position = self.get_min_proof_node(begin);
        let end_position = self.get_min_proof_node(end);
        for position in begin_position.to_postorder_index()..end_position.to_postorder_index() {
            db_batch.delete::<TransactionAccumulatorSchema>(&Position::from_postorder_index(
                position,
            )?)?;
        }
        Ok(())
    }

    /// Returns the minimum position node needed to be included in the proof of the leaf index. This
    /// will be the left child of the root if the leaf index is non zero and zero otherwise.
    pub fn get_min_proof_node(&self, leaf_index: u64) -> Position {
        if leaf_index > 0 {
            Position::root_from_leaf_index(leaf_index).left_child()
        } else {
            // Handle this as a special case when min_readable_version is 0
            Position::root_from_leaf_index(0)
        }
    }
}

pub struct TransactionIter<'a> {
    inner: SchemaIterator<'a, TransactionSchema>,
    expected_next_version: Version,
    end_version: Version,
}

impl<'a> TransactionIter<'a> {
    fn next_impl(&mut self) -> Result<Option<Transaction>> {
        if self.expected_next_version >= self.end_version {
            return Ok(None);
        }

        let ret = match self.inner.next().transpose()? {
            Some((version, transaction)) => {
                ensure!(
                    version == self.expected_next_version,
                    "Transaction versions are not consecutive.",
                );
                self.expected_next_version += 1;
                Some(transaction)
            }
            None => None,
        };

        Ok(ret)
    }
}

impl<'a> Iterator for TransactionIter<'a> {
    type Item = Result<Transaction>;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_impl().transpose()
    }
}

// TODO(philiphayes): this will need to change to support CRSNs
// (Conflict-Resistant Sequence Numbers)[https://github.com/diem/dip/blob/main/dips/dip-168.md].
//
// It depends on the implementation details, but we'll probably index by _requested_
// transaction sequence number rather than committed account sequence number.
// This would mean the property: `seq_num_{i+1} == seq_num_{i} + 1` would no longer
// be guaranteed and the check should be removed.
//
// This index would also no longer iterate over an account's transactions in
// committed order, meaning the outer method would need to overread by
// `CRSN_WINDOW_SIZE`, sort by version, and take only `limit` entries to get
// at most `limit` transactions in committed order. Alternatively, add another
// index for scanning an accounts transactions in committed order, e.g.,
// `(AccountAddress, Version) -> SeqNum`.

pub struct AccountTransactionVersionIter<'a> {
    inner: SchemaIterator<'a, TransactionByAccountSchema>,
    address: AccountAddress,
    expected_next_seq_num: u64,
    end_seq_num: u64,
    prev_version: Option<Version>,
    ledger_version: Version,
}

impl<'a> AccountTransactionVersionIter<'a> {
    fn next_impl(&mut self) -> Result<Option<(u64, Version)>> {
        if self.expected_next_seq_num >= self.end_seq_num {
            return Ok(None);
        }

        Ok(match self.inner.next().transpose()? {
            Some(((address, seq_num), version)) => {
                // No more transactions sent by this account.
                if address != self.address {
                    return Ok(None);
                }

                // Ensure seq_num_{i+1} == seq_num_{i} + 1
                ensure!(
                    seq_num == self.expected_next_seq_num,
                    "DB corruption: account transactions sequence numbers are not contiguous: \
                     actual: {}, expected: {}",
                    seq_num,
                    self.expected_next_seq_num,
                );

                // Ensure version_{i+1} > version_{i}
                if let Some(prev_version) = self.prev_version {
                    ensure!(
                        prev_version < version,
                        "DB corruption: account transaction versions are not strictly increasing: \
                         previous version: {}, current version: {}",
                        prev_version,
                        version,
                    );
                }

                // No more transactions (in this view of the ledger).
                if version > self.ledger_version {
                    return Ok(None);
                }

                self.expected_next_seq_num += 1;
                self.prev_version = Some(version);
                Some((seq_num, version))
            }
            None => None,
        })
    }
}

impl<'a> Iterator for AccountTransactionVersionIter<'a> {
    type Item = Result<(u64, Version)>;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_impl().transpose()
    }
}

#[cfg(test)]
mod test;
<commit_msg>[Storage][Pruner] Do not error out in get_account_transaction_version_iter for pruned transaction<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! This file defines transaction store APIs that are related to committed signed transactions.

use crate::{
    change_set::ChangeSet,
    errors::AptosDbError,
    schema::{
        transaction::TransactionSchema, transaction_by_account::TransactionByAccountSchema,
        transaction_by_hash::TransactionByHashSchema, write_set::WriteSetSchema,
    },
    transaction_accumulator::TransactionAccumulatorSchema,
    transaction_info::TransactionInfoSchema,
};
use anyhow::{ensure, format_err, Result};
use aptos_crypto::{hash::CryptoHash, HashValue};
use aptos_types::{
    account_address::AccountAddress,
    block_metadata::BlockMetadata,
    proof::position::Position,
    transaction::{Transaction, Version},
    write_set::WriteSet,
};
use schemadb::{ReadOptions, SchemaBatch, SchemaIterator, DB};
use std::sync::Arc;

#[derive(Clone, Debug)]
pub struct TransactionStore {
    db: Arc<DB>,
}

impl TransactionStore {
    pub fn new(db: Arc<DB>) -> Self {
        Self { db }
    }

    /// Gets the version of a transaction by the sender `address` and `sequence_number`.
    pub fn get_account_transaction_version(
        &self,
        address: AccountAddress,
        sequence_number: u64,
        ledger_version: Version,
    ) -> Result<Option<Version>> {
        if let Some(version) = self
            .db
            .get::<TransactionByAccountSchema>(&(address, sequence_number))?
        {
            if version <= ledger_version {
                return Ok(Some(version));
            }
        }

        Ok(None)
    }

    /// Gets the version of a transaction by its hash.
    pub fn get_transaction_version_by_hash(
        &self,
        hash: &HashValue,
        ledger_version: Version,
    ) -> Result<Option<Version>> {
        Ok(match self.db.get::<TransactionByHashSchema>(hash)? {
            Some(version) if version <= ledger_version => Some(version),
            _ => None,
        })
    }

    /// Gets an iterator that yields `(sequence_number, version)` for each
    /// transaction sent by an account, with minimum sequence number greater
    /// `min_seq_num`, and returning at most `num_versions` results with
    /// `version <= ledger_version`.
    /// Guarantees that the returned sequence numbers are sequential, i.e.,
    /// `seq_num_{i} + 1 = seq_num_{i+1}`.
    pub fn get_account_transaction_version_iter(
        &self,
        address: AccountAddress,
        min_seq_num: u64,
        num_versions: u64,
        ledger_version: Version,
    ) -> Result<AccountTransactionVersionIter> {
        let mut iter = self
            .db
            .iter::<TransactionByAccountSchema>(ReadOptions::default())?;
        iter.seek(&(address, min_seq_num))?;
        Ok(AccountTransactionVersionIter {
            inner: iter,
            address,
            expected_next_seq_num: None,
            end_seq_num: min_seq_num
                .checked_add(num_versions)
                .ok_or_else(|| format_err!("too many transactions requested"))?,
            prev_version: None,
            ledger_version,
        })
    }

    /// Get signed transaction given `version`
    pub fn get_transaction(&self, version: Version) -> Result<Transaction> {
        self.db
            .get::<TransactionSchema>(&version)?
            .ok_or_else(|| AptosDbError::NotFound(format!("Txn {}", version)).into())
    }

    /// Gets an iterator that yields `num_transactions` transactions starting from `start_version`.
    pub fn get_transaction_iter(
        &self,
        start_version: Version,
        num_transactions: usize,
    ) -> Result<TransactionIter> {
        let mut iter = self.db.iter::<TransactionSchema>(ReadOptions::default())?;
        iter.seek(&start_version)?;
        Ok(TransactionIter {
            inner: iter,
            expected_next_version: start_version,
            end_version: start_version
                .checked_add(num_transactions as u64)
                .ok_or_else(|| format_err!("too many transactions requested"))?,
        })
    }

    /// Get the first version that txn starts existent.
    pub fn get_first_txn_version(&self) -> Result<Option<Version>> {
        let mut iter = self.db.iter::<TransactionSchema>(Default::default())?;
        iter.seek_to_first();
        iter.next().map(|res| res.map(|(v, _)| v)).transpose()
    }

    /// Returns the block metadata carried on the block metadata transaction at or preceding
    /// `version`, together with the version of the block metadata transaction.
    /// Returns None if there's no such transaction at or preceding `version` (it's likely the genesis
    /// version 0).
    pub fn get_block_metadata(&self, version: Version) -> Result<Option<(Version, BlockMetadata)>> {
        // Must be larger than a block size, otherwise a NotFound error will be raised wrongly.
        const MAX_VERSIONS_TO_SEARCH: usize = 1000 * 100;

        // Linear search via `DB::rev_iter()` here, NOT expecting performance hit, due to the fact
        // that the iterator caches data block and that there are limited number of transactions in
        // each block.
        let mut iter = self.db.rev_iter::<TransactionSchema>(Default::default())?;
        iter.seek(&version)?;
        for res in iter.take(MAX_VERSIONS_TO_SEARCH) {
            let (v, txn) = res?;
            if let Transaction::BlockMetadata(block_meta) = txn {
                return Ok(Some((v, block_meta)));
            } else if v == 0 {
                return Ok(None);
            }
        }

        Err(AptosDbError::NotFound(format!("BlockMetadata preceding version {}", version)).into())
    }

    /// Save signed transaction at `version`
    pub fn put_transaction(
        &self,
        version: Version,
        transaction: &Transaction,
        cs: &mut ChangeSet,
    ) -> Result<()> {
        if let Transaction::UserTransaction(txn) = transaction {
            cs.batch.put::<TransactionByAccountSchema>(
                &(txn.sender(), txn.sequence_number()),
                &version,
            )?;
        }
        cs.batch
            .put::<TransactionByHashSchema>(&transaction.hash(), &version)?;
        cs.batch.put::<TransactionSchema>(&version, transaction)?;

        Ok(())
    }

    /// Get executed transaction vm output given `version`
    pub fn get_write_set(&self, version: Version) -> Result<WriteSet> {
        self.db.get::<WriteSetSchema>(&version)?.ok_or_else(|| {
            AptosDbError::NotFound(format!("WriteSet at version {}", version)).into()
        })
    }

    /// Get write sets in `[begin_version, end_version)` half-open range.
    ///
    /// N.b. an empty `Vec` is returned when `begin_version == end_version`
    pub fn get_write_sets(
        &self,
        begin_version: Version,
        end_version: Version,
    ) -> Result<Vec<WriteSet>> {
        if begin_version == end_version {
            return Ok(Vec::new());
        }
        ensure!(
            begin_version < end_version,
            "begin_version {} >= end_version {}",
            begin_version,
            end_version
        );

        let mut iter = self.db.iter::<WriteSetSchema>(Default::default())?;
        iter.seek(&begin_version)?;

        let mut ret = Vec::with_capacity((end_version - begin_version) as usize);
        for current_version in begin_version..end_version {
            let (version, write_set) = iter
                .next()
                .transpose()?
                .ok_or_else(|| format_err!("Write set missing for version {}", current_version))?;
            ensure!(
                version == current_version,
                "Write set missing for version {}, got version {}",
                current_version,
                version,
            );
            ret.push(write_set);
        }

        Ok(ret)
    }

    /// Get the first version that write set starts existent.
    pub fn get_first_write_set_version(&self) -> Result<Option<Version>> {
        let mut iter = self.db.iter::<WriteSetSchema>(Default::default())?;
        iter.seek_to_first();
        iter.next().map(|res| res.map(|(v, _)| v)).transpose()
    }

    /// Save executed transaction vm output given `version`
    pub fn put_write_set(
        &self,
        version: Version,
        write_set: &WriteSet,
        cs: &mut ChangeSet,
    ) -> Result<()> {
        cs.batch.put::<WriteSetSchema>(&version, write_set)
    }

    /// Prune the transaction by hash store given a list of transaction
    pub fn prune_transaction_by_hash(
        &self,
        transactions: &[Transaction],
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for transaction in transactions {
            db_batch.delete::<TransactionByHashSchema>(&transaction.hash())?;
        }
        Ok(())
    }

    /// Prune the transaction by account store given a list of transaction
    pub fn prune_transaction_by_account(
        &self,
        transactions: &[Transaction],
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for transaction in transactions {
            if let Transaction::UserTransaction(txn) = transaction {
                db_batch
                    .delete::<TransactionByAccountSchema>(&(txn.sender(), txn.sequence_number()))?;
            }
        }
        Ok(())
    }

    /// Prune the transaction schema store between a range of version in [begin, end)
    pub fn prune_transaction_schema(
        &self,
        begin: Version,
        end: Version,
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for version in begin..end {
            db_batch.delete::<TransactionSchema>(&version)?;
        }
        Ok(())
    }

    /// Prune the transaction schema store between a range of version in [begin, end)
    pub fn prune_transaction_info_schema(
        &self,
        begin: Version,
        end: Version,
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for version in begin..end {
            db_batch.delete::<TransactionInfoSchema>(&version)?;
        }
        Ok(())
    }

    /// Prune the transaction schema store between a range of version in [begin, end)
    pub fn prune_write_set(
        &self,
        begin: Version,
        end: Version,
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        for version in begin..end {
            db_batch.delete::<WriteSetSchema>(&version)?;
        }
        Ok(())
    }

    /// Prune the transaction schema store between a range of version in [begin, end).
    pub fn prune_transaction_accumulator(
        &self,
        begin: Version,
        end: Version,
        db_batch: &mut SchemaBatch,
    ) -> anyhow::Result<()> {
        let begin_position = self.get_min_proof_node(begin);
        let end_position = self.get_min_proof_node(end);
        for position in begin_position.to_postorder_index()..end_position.to_postorder_index() {
            db_batch.delete::<TransactionAccumulatorSchema>(&Position::from_postorder_index(
                position,
            )?)?;
        }
        Ok(())
    }

    /// Returns the minimum position node needed to be included in the proof of the leaf index. This
    /// will be the left child of the root if the leaf index is non zero and zero otherwise.
    pub fn get_min_proof_node(&self, leaf_index: u64) -> Position {
        if leaf_index > 0 {
            Position::root_from_leaf_index(leaf_index).left_child()
        } else {
            // Handle this as a special case when min_readable_version is 0
            Position::root_from_leaf_index(0)
        }
    }
}

pub struct TransactionIter<'a> {
    inner: SchemaIterator<'a, TransactionSchema>,
    expected_next_version: Version,
    end_version: Version,
}

impl<'a> TransactionIter<'a> {
    fn next_impl(&mut self) -> Result<Option<Transaction>> {
        if self.expected_next_version >= self.end_version {
            return Ok(None);
        }

        let ret = match self.inner.next().transpose()? {
            Some((version, transaction)) => {
                ensure!(
                    version == self.expected_next_version,
                    "Transaction versions are not consecutive.",
                );
                self.expected_next_version += 1;
                Some(transaction)
            }
            None => None,
        };

        Ok(ret)
    }
}

impl<'a> Iterator for TransactionIter<'a> {
    type Item = Result<Transaction>;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_impl().transpose()
    }
}

// TODO(philiphayes): this will need to change to support CRSNs
// (Conflict-Resistant Sequence Numbers)[https://github.com/diem/dip/blob/main/dips/dip-168.md].
//
// It depends on the implementation details, but we'll probably index by _requested_
// transaction sequence number rather than committed account sequence number.
// This would mean the property: `seq_num_{i+1} == seq_num_{i} + 1` would no longer
// be guaranteed and the check should be removed.
//
// This index would also no longer iterate over an account's transactions in
// committed order, meaning the outer method would need to overread by
// `CRSN_WINDOW_SIZE`, sort by version, and take only `limit` entries to get
// at most `limit` transactions in committed order. Alternatively, add another
// index for scanning an accounts transactions in committed order, e.g.,
// `(AccountAddress, Version) -> SeqNum`.

pub struct AccountTransactionVersionIter<'a> {
    inner: SchemaIterator<'a, TransactionByAccountSchema>,
    address: AccountAddress,
    expected_next_seq_num: Option<u64>,
    end_seq_num: u64,
    prev_version: Option<Version>,
    ledger_version: Version,
}

impl<'a> AccountTransactionVersionIter<'a> {
    fn next_impl(&mut self) -> Result<Option<(u64, Version)>> {
        Ok(match self.inner.next().transpose()? {
            Some(((address, seq_num), version)) => {
                // No more transactions sent by this account.
                if address != self.address {
                    return Ok(None);
                }
                if seq_num >= self.end_seq_num {
                    return Ok(None);
                }

                // Ensure seq_num_{i+1} == seq_num_{i} + 1
                if let Some(expected_seq_num) = self.expected_next_seq_num {
                    ensure!(
                        seq_num == expected_seq_num,
                        "DB corruption: account transactions sequence numbers are not contiguous: \
                     actual: {}, expected: {}",
                        seq_num,
                        expected_seq_num,
                    );
                };

                // Ensure version_{i+1} > version_{i}
                if let Some(prev_version) = self.prev_version {
                    ensure!(
                        prev_version < version,
                        "DB corruption: account transaction versions are not strictly increasing: \
                         previous version: {}, current version: {}",
                        prev_version,
                        version,
                    );
                }

                // No more transactions (in this view of the ledger).
                if version > self.ledger_version {
                    return Ok(None);
                }

                self.expected_next_seq_num = Some(seq_num + 1);
                self.prev_version = Some(version);
                Some((seq_num, version))
            }
            None => None,
        })
    }
}

impl<'a> Iterator for AccountTransactionVersionIter<'a> {
    type Item = Result<(u64, Version)>;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_impl().transpose()
    }
}

#[cfg(test)]
mod test;
<|endoftext|>


<commit_before>// Copyright (C) 2015 Élisabeth HENRY.
//
// This file is part of Caribon.
//
// Caribon is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Caribon.  If not, see <http://www.gnu.org/licenses/>.

extern crate caribon;
mod config;
use config::Config;
use caribon::Parser;

use std::error::Error;
use std::io::Read;
use std::collections::HashMap;

fn print_stats(h: &HashMap<String, f32>, n_words: u32) {
    let different_words = h.len();
    println!("Number of words: {}", n_words);
    println!("Number of different words: {}", different_words);
}

fn try_parse() -> Result<(), Box<Error>> {
    let mut config = Config::new_from_args();
    let mut parser = try!(Parser::new(&config.lang));

    parser = parser.with_html(&config.input_format == "html")
        .with_fuzzy(config.fuzzy)
        .with_ignore_proper(config.ignore_proper)
        .with_max_distance(config.max_distance);

    if !config.ignored.is_empty() {
        parser = parser.with_ignored(&config.ignored);
    }
    if !config.add_ignored.is_empty() {
        parser = parser.with_more_ignored(&config.add_ignored);
    }

    let mut s = String::new();
    try!(config.input.read_to_string(&mut s));

    let mut ast = try!(parser.tokenize(&s));
    if config.print_stats {
        let (h, count) = parser.words_stats(&ast);
        print_stats(&h, count);
    }
    parser.detect_local(&mut ast, config.threshold);
    if let Some(threshold) = config.global_threshold {
        parser.detect_global(&mut ast, threshold);
    }
    let output = match &*config.output_format {
        "html" => parser.ast_to_html(&mut ast, true),
        "terminal" => parser.ast_to_terminal(&ast),
        "markdown" => parser.ast_to_markdown(&ast),
        _ => return Err(Box::new(caribon::Error::new("Wrong output format: must be 'html, 'terminal', or 'markdown'")))
    };
    try!(config.output.write(&output.bytes().collect::<Vec<u8>>()));
    Ok(())
}

fn main() {
    match try_parse () {
        Ok(_) => {},
        Err(e) => println!("{}", e.description())
    }
}
<commit_msg>runned rustfmt on main.rs<commit_after>// Copyright (C) 2015 Élisabeth HENRY.
//
// This file is part of Caribon.
//
// Caribon is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Caribon.  If not, see <http://www.gnu.org/licenses/>.

extern crate caribon;
mod config;
use config::Config;
use caribon::Parser;

use std::error::Error;
use std::io::Read;
use std::collections::HashMap;

fn print_stats(h: &HashMap<String, f32>, n_words: u32) {
    let different_words = h.len();
    println!("Number of words: {}", n_words);
    println!("Number of different words: {}", different_words);
}

fn try_parse() -> Result<(), Box<Error>> {
    let mut config = Config::new_from_args();
    let mut parser = try!(Parser::new(&config.lang));

    parser = parser.with_html(&config.input_format == "html")
                   .with_fuzzy(config.fuzzy)
                   .with_ignore_proper(config.ignore_proper)
                   .with_max_distance(config.max_distance);

    if !config.ignored.is_empty() {
        parser = parser.with_ignored(&config.ignored);
    }
    if !config.add_ignored.is_empty() {
        parser = parser.with_more_ignored(&config.add_ignored);
    }

    let mut s = String::new();
    try!(config.input.read_to_string(&mut s));

    let mut ast = try!(parser.tokenize(&s));
    if config.print_stats {
        let (h, count) = parser.words_stats(&ast);
        print_stats(&h, count);
    }
    parser.detect_local(&mut ast, config.threshold);
    if let Some(threshold) = config.global_threshold {
        parser.detect_global(&mut ast, threshold);
    }
    let output = match &*config.output_format {
        "html" => parser.ast_to_html(&mut ast, true),
        "terminal" => parser.ast_to_terminal(&ast),
        "markdown" => parser.ast_to_markdown(&ast),
        _ => return Err(Box::new(caribon::Error::new("Wrong output format: must be 'html, \
                                                      'terminal', or 'markdown'"))),
    };
    try!(config.output.write(&output.bytes().collect::<Vec<u8>>()));
    Ok(())
}

fn main() {
    match try_parse() {
        Ok(_) => {}
        Err(e) => println!("{}", e.description()),
    }
}
<|endoftext|>


<commit_before>use rustc::lint::{LintArray, LateLintPass, LateContext, LintPass};
use rustc_front::hir::*;
use rustc_front::intravisit::*;
use syntax::ast::LitKind;
use utils::{span_lint_and_then, in_macro, snippet_opt, SpanlessEq};

/// **What it does:** This lint checks for boolean expressions that can be written more concisely
///
/// **Why is this bad?** Readability of boolean expressions suffers from unnecesessary duplication
///
/// **Known problems:** Ignores short circuting behavior, bitwise and/or and xor. Ends up suggesting things like !(a == b)
///
/// **Example:** `if a && true` should be `if a`
declare_lint! {
    pub NONMINIMAL_BOOL, Warn,
    "checks for boolean expressions that can be written more concisely"
}

/// **What it does:** This lint checks for boolean expressions that contain terminals that can be eliminated
///
/// **Why is this bad?** This is most likely a logic bug
///
/// **Known problems:** Ignores short circuiting behavior
///
/// **Example:** The `b` in `if a && b || a` is unnecessary because the expression is equivalent to `if a`
declare_lint! {
    pub LOGIC_BUG, Warn,
    "checks for boolean expressions that contain terminals which can be eliminated"
}

#[derive(Copy,Clone)]
pub struct NonminimalBool;

impl LintPass for NonminimalBool {
    fn get_lints(&self) -> LintArray {
        lint_array!(NONMINIMAL_BOOL, LOGIC_BUG)
    }
}

impl LateLintPass for NonminimalBool {
    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        NonminimalBoolVisitor(cx).visit_item(item)
    }
}

struct NonminimalBoolVisitor<'a, 'tcx: 'a>(&'a LateContext<'a, 'tcx>);

use quine_mc_cluskey::Bool;
struct Hir2Qmm<'a, 'tcx: 'a, 'v> {
    terminals: Vec<&'v Expr>,
    cx: &'a LateContext<'a, 'tcx>
}

impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
    fn extract(&mut self, op: BinOp_, a: &[&'v Expr], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
        for a in a {
            if let ExprBinary(binop, ref lhs, ref rhs) = a.node {
                if binop.node == op {
                    v = self.extract(op, &[lhs, rhs], v)?;
                    continue;
                }
            }
            v.push(self.run(a)?);
        }
        Ok(v)
    }

    fn run(&mut self, e: &'v Expr) -> Result<Bool, String> {
        // prevent folding of `cfg!` macros and the like
        if !in_macro(self.cx, e.span) {
            match e.node {
                ExprUnary(UnNot, ref inner) => return Ok(Bool::Not(box self.run(inner)?)),
                ExprBinary(binop, ref lhs, ref rhs) => {
                    match binop.node {
                        BiOr => return Ok(Bool::Or(self.extract(BiOr, &[lhs, rhs], Vec::new())?)),
                        BiAnd => return Ok(Bool::And(self.extract(BiAnd, &[lhs, rhs], Vec::new())?)),
                        _ => {},
                    }
                },
                ExprLit(ref lit) => {
                    match lit.node {
                        LitKind::Bool(true) => return Ok(Bool::True),
                        LitKind::Bool(false) => return Ok(Bool::False),
                        _ => {},
                    }
                },
                _ => {},
            }
        }
        if let Some((n, _)) = self.terminals
                                  .iter()
                                  .enumerate()
                                  .find(|&(_, expr)| SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr)) {
            #[allow(cast_possible_truncation)]
            return Ok(Bool::Term(n as u8));
        }
        let n = self.terminals.len();
        self.terminals.push(e);
        if n < 32 {
            #[allow(cast_possible_truncation)]
            Ok(Bool::Term(n as u8))
        } else {
            Err("too many literals".to_owned())
        }
    }
}

fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
    fn recurse(brackets: bool, cx: &LateContext, suggestion: &Bool, terminals: &[&Expr], mut s: String) -> String {
        use quine_mc_cluskey::Bool::*;
        match *suggestion {
            True => {
                s.push_str("true");
                s
            },
            False => {
                s.push_str("false");
                s
            },
            Not(ref inner) => {
                s.push('!');
                recurse(true, cx, inner, terminals, s)
            },
            And(ref v) => {
                if brackets {
                    s.push('(');
                }
                s = recurse(true, cx, &v[0], terminals, s);
                for inner in &v[1..] {
                    s.push_str(" && ");
                    s = recurse(true, cx, inner, terminals, s);
                }
                if brackets {
                    s.push(')');
                }
                s
            },
            Or(ref v) => {
                if brackets {
                    s.push('(');
                }
                s = recurse(true, cx, &v[0], terminals, s);
                for inner in &v[1..] {
                    s.push_str(" || ");
                    s = recurse(true, cx, inner, terminals, s);
                }
                if brackets {
                    s.push(')');
                }
                s
            },
            Term(n) => {
                s.push_str(&snippet_opt(cx, terminals[n as usize].span).expect("don't try to improve booleans created by macros"));
                s
            }
        }
    }
    recurse(false, cx, suggestion, terminals, String::new())
}

fn simple_negate(b: Bool) -> Bool {
    use quine_mc_cluskey::Bool::*;
    match b {
        True => False,
        False => True,
        t @ Term(_) => Not(Box::new(t)),
        And(mut v) => {
            for el in &mut v {
                *el = simple_negate(::std::mem::replace(el, True));
            }
            Or(v)
        },
        Or(mut v) => {
            for el in &mut v {
                *el = simple_negate(::std::mem::replace(el, True));
            }
            And(v)
        },
        Not(inner) => *inner,
    }
}

#[derive(Default)]
struct Stats {
    terminals: [usize; 32],
    negations: usize,
    ops: usize,
}

fn terminal_stats(b: &Bool) -> Stats {
    fn recurse(b: &Bool, stats: &mut Stats) {
        match *b {
            True | False => stats.ops += 1,
            Not(ref inner) => {
                match **inner {
                    And(_) | Or(_) => stats.ops += 1, // brackets are also operations
                    _ => stats.negations += 1,
                }
                recurse(inner, stats);
            },
            And(ref v) | Or(ref v) => {
                stats.ops += v.len() - 1;
                for inner in v {
                    recurse(inner, stats);
                }
            },
            Term(n) => stats.terminals[n as usize] += 1,
        }
    }
    use quine_mc_cluskey::Bool::*;
    let mut stats = Stats::default();
    recurse(b, &mut stats);
    stats
}

impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
    fn bool_expr(&self, e: &Expr) {
        let mut h2q = Hir2Qmm {
            terminals: Vec::new(),
            cx: self.0,
        };
        if let Ok(expr) = h2q.run(e) {
            let stats = terminal_stats(&expr);
            let mut simplified = expr.simplify();
            for simple in Bool::Not(Box::new(expr.clone())).simplify() {
                match simple {
                    Bool::Not(_) | Bool::True | Bool::False => {},
                    _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
                }
                let simple_negated = simple_negate(simple);
                if simplified.iter().any(|s| *s == simple_negated) {
                    continue;
                }
                simplified.push(simple_negated);
            }
            let mut improvements = Vec::new();
            'simplified: for suggestion in &simplified {
                let simplified_stats = terminal_stats(&suggestion);
                let mut improvement = false;
                for i in 0..32 {
                    // ignore any "simplifications" that end up requiring a terminal more often than in the original expression
                    if stats.terminals[i] < simplified_stats.terminals[i] {
                        continue 'simplified;
                    }
                    if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
                        span_lint_and_then(self.0, LOGIC_BUG, e.span, "this boolean expression contains a logic bug", |db| {
                            db.span_help(h2q.terminals[i].span, "this expression can be optimized out by applying boolean operations to the outer expression");
                            db.span_suggestion(e.span, "it would look like the following", suggest(self.0, suggestion, &h2q.terminals));
                        });
                        // don't also lint `NONMINIMAL_BOOL`
                        return;
                    }
                    // if the number of occurrences of a terminal decreases or any of the stats decreases while none increases
                    improvement = (stats.terminals[i] > simplified_stats.terminals[i]) ||
                        (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops) ||
                        (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
                }
                if improvement {
                    improvements.push(suggestion);
                }
            }
            if !improvements.is_empty() {
                span_lint_and_then(self.0, NONMINIMAL_BOOL, e.span, "this boolean expression can be simplified", |db| {
                    for suggestion in &improvements {
                        db.span_suggestion(e.span, "try", suggest(self.0, suggestion, &h2q.terminals));
                    }
                });
            }
        }
    }
}

impl<'a, 'v, 'tcx> Visitor<'v> for NonminimalBoolVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, e: &'v Expr) {
        if in_macro(self.0, e.span) { return }
        match e.node {
            ExprBinary(binop, _, _) if binop.node == BiOr || binop.node == BiAnd => self.bool_expr(e),
            ExprUnary(UnNot, ref inner) => {
                if self.0.tcx.node_types()[&inner.id].is_bool() {
                    self.bool_expr(e);
                } else {
                    walk_expr(self, e);
                }
            },
            _ => walk_expr(self, e),
        }
    }
}
<commit_msg>a small refactoring for readability<commit_after>use rustc::lint::{LintArray, LateLintPass, LateContext, LintPass};
use rustc_front::hir::*;
use rustc_front::intravisit::*;
use syntax::ast::LitKind;
use utils::{span_lint_and_then, in_macro, snippet_opt, SpanlessEq};

/// **What it does:** This lint checks for boolean expressions that can be written more concisely
///
/// **Why is this bad?** Readability of boolean expressions suffers from unnecesessary duplication
///
/// **Known problems:** Ignores short circuting behavior, bitwise and/or and xor. Ends up suggesting things like !(a == b)
///
/// **Example:** `if a && true` should be `if a`
declare_lint! {
    pub NONMINIMAL_BOOL, Warn,
    "checks for boolean expressions that can be written more concisely"
}

/// **What it does:** This lint checks for boolean expressions that contain terminals that can be eliminated
///
/// **Why is this bad?** This is most likely a logic bug
///
/// **Known problems:** Ignores short circuiting behavior
///
/// **Example:** The `b` in `if a && b || a` is unnecessary because the expression is equivalent to `if a`
declare_lint! {
    pub LOGIC_BUG, Warn,
    "checks for boolean expressions that contain terminals which can be eliminated"
}

#[derive(Copy,Clone)]
pub struct NonminimalBool;

impl LintPass for NonminimalBool {
    fn get_lints(&self) -> LintArray {
        lint_array!(NONMINIMAL_BOOL, LOGIC_BUG)
    }
}

impl LateLintPass for NonminimalBool {
    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        NonminimalBoolVisitor(cx).visit_item(item)
    }
}

struct NonminimalBoolVisitor<'a, 'tcx: 'a>(&'a LateContext<'a, 'tcx>);

use quine_mc_cluskey::Bool;
struct Hir2Qmm<'a, 'tcx: 'a, 'v> {
    terminals: Vec<&'v Expr>,
    cx: &'a LateContext<'a, 'tcx>
}

impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
    fn extract(&mut self, op: BinOp_, a: &[&'v Expr], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
        for a in a {
            if let ExprBinary(binop, ref lhs, ref rhs) = a.node {
                if binop.node == op {
                    v = self.extract(op, &[lhs, rhs], v)?;
                    continue;
                }
            }
            v.push(self.run(a)?);
        }
        Ok(v)
    }

    fn run(&mut self, e: &'v Expr) -> Result<Bool, String> {
        // prevent folding of `cfg!` macros and the like
        if !in_macro(self.cx, e.span) {
            match e.node {
                ExprUnary(UnNot, ref inner) => return Ok(Bool::Not(box self.run(inner)?)),
                ExprBinary(binop, ref lhs, ref rhs) => {
                    match binop.node {
                        BiOr => return Ok(Bool::Or(self.extract(BiOr, &[lhs, rhs], Vec::new())?)),
                        BiAnd => return Ok(Bool::And(self.extract(BiAnd, &[lhs, rhs], Vec::new())?)),
                        _ => {},
                    }
                },
                ExprLit(ref lit) => {
                    match lit.node {
                        LitKind::Bool(true) => return Ok(Bool::True),
                        LitKind::Bool(false) => return Ok(Bool::False),
                        _ => {},
                    }
                },
                _ => {},
            }
        }
        for (n, expr) in self.terminals.iter().enumerate() {
            if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
                #[allow(cast_possible_truncation)]
                return Ok(Bool::Term(n as u8));
            }
        }
        let n = self.terminals.len();
        self.terminals.push(e);
        if n < 32 {
            #[allow(cast_possible_truncation)]
            Ok(Bool::Term(n as u8))
        } else {
            Err("too many literals".to_owned())
        }
    }
}

fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
    fn recurse(brackets: bool, cx: &LateContext, suggestion: &Bool, terminals: &[&Expr], mut s: String) -> String {
        use quine_mc_cluskey::Bool::*;
        match *suggestion {
            True => {
                s.push_str("true");
                s
            },
            False => {
                s.push_str("false");
                s
            },
            Not(ref inner) => {
                s.push('!');
                recurse(true, cx, inner, terminals, s)
            },
            And(ref v) => {
                if brackets {
                    s.push('(');
                }
                s = recurse(true, cx, &v[0], terminals, s);
                for inner in &v[1..] {
                    s.push_str(" && ");
                    s = recurse(true, cx, inner, terminals, s);
                }
                if brackets {
                    s.push(')');
                }
                s
            },
            Or(ref v) => {
                if brackets {
                    s.push('(');
                }
                s = recurse(true, cx, &v[0], terminals, s);
                for inner in &v[1..] {
                    s.push_str(" || ");
                    s = recurse(true, cx, inner, terminals, s);
                }
                if brackets {
                    s.push(')');
                }
                s
            },
            Term(n) => {
                s.push_str(&snippet_opt(cx, terminals[n as usize].span).expect("don't try to improve booleans created by macros"));
                s
            }
        }
    }
    recurse(false, cx, suggestion, terminals, String::new())
}

fn simple_negate(b: Bool) -> Bool {
    use quine_mc_cluskey::Bool::*;
    match b {
        True => False,
        False => True,
        t @ Term(_) => Not(Box::new(t)),
        And(mut v) => {
            for el in &mut v {
                *el = simple_negate(::std::mem::replace(el, True));
            }
            Or(v)
        },
        Or(mut v) => {
            for el in &mut v {
                *el = simple_negate(::std::mem::replace(el, True));
            }
            And(v)
        },
        Not(inner) => *inner,
    }
}

#[derive(Default)]
struct Stats {
    terminals: [usize; 32],
    negations: usize,
    ops: usize,
}

fn terminal_stats(b: &Bool) -> Stats {
    fn recurse(b: &Bool, stats: &mut Stats) {
        match *b {
            True | False => stats.ops += 1,
            Not(ref inner) => {
                match **inner {
                    And(_) | Or(_) => stats.ops += 1, // brackets are also operations
                    _ => stats.negations += 1,
                }
                recurse(inner, stats);
            },
            And(ref v) | Or(ref v) => {
                stats.ops += v.len() - 1;
                for inner in v {
                    recurse(inner, stats);
                }
            },
            Term(n) => stats.terminals[n as usize] += 1,
        }
    }
    use quine_mc_cluskey::Bool::*;
    let mut stats = Stats::default();
    recurse(b, &mut stats);
    stats
}

impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
    fn bool_expr(&self, e: &Expr) {
        let mut h2q = Hir2Qmm {
            terminals: Vec::new(),
            cx: self.0,
        };
        if let Ok(expr) = h2q.run(e) {
            let stats = terminal_stats(&expr);
            let mut simplified = expr.simplify();
            for simple in Bool::Not(Box::new(expr.clone())).simplify() {
                match simple {
                    Bool::Not(_) | Bool::True | Bool::False => {},
                    _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
                }
                let simple_negated = simple_negate(simple);
                if simplified.iter().any(|s| *s == simple_negated) {
                    continue;
                }
                simplified.push(simple_negated);
            }
            let mut improvements = Vec::new();
            'simplified: for suggestion in &simplified {
                let simplified_stats = terminal_stats(&suggestion);
                let mut improvement = false;
                for i in 0..32 {
                    // ignore any "simplifications" that end up requiring a terminal more often than in the original expression
                    if stats.terminals[i] < simplified_stats.terminals[i] {
                        continue 'simplified;
                    }
                    if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
                        span_lint_and_then(self.0, LOGIC_BUG, e.span, "this boolean expression contains a logic bug", |db| {
                            db.span_help(h2q.terminals[i].span, "this expression can be optimized out by applying boolean operations to the outer expression");
                            db.span_suggestion(e.span, "it would look like the following", suggest(self.0, suggestion, &h2q.terminals));
                        });
                        // don't also lint `NONMINIMAL_BOOL`
                        return;
                    }
                    // if the number of occurrences of a terminal decreases or any of the stats decreases while none increases
                    improvement = (stats.terminals[i] > simplified_stats.terminals[i]) ||
                        (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops) ||
                        (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
                }
                if improvement {
                    improvements.push(suggestion);
                }
            }
            if !improvements.is_empty() {
                span_lint_and_then(self.0, NONMINIMAL_BOOL, e.span, "this boolean expression can be simplified", |db| {
                    for suggestion in &improvements {
                        db.span_suggestion(e.span, "try", suggest(self.0, suggestion, &h2q.terminals));
                    }
                });
            }
        }
    }
}

impl<'a, 'v, 'tcx> Visitor<'v> for NonminimalBoolVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, e: &'v Expr) {
        if in_macro(self.0, e.span) { return }
        match e.node {
            ExprBinary(binop, _, _) if binop.node == BiOr || binop.node == BiAnd => self.bool_expr(e),
            ExprUnary(UnNot, ref inner) => {
                if self.0.tcx.node_types()[&inner.id].is_bool() {
                    self.bool_expr(e);
                } else {
                    walk_expr(self, e);
                }
            },
            _ => walk_expr(self, e),
        }
    }
}
<|endoftext|>


<commit_before>//! The main rustup commandline application
//!
//! The rustup binary is a chimera, changing its behavior based on the
//! name of the binary. This is used most prominently to enable
//! rustup's tool 'proxies' - that is, rustup itself and the rustup
//! proxies are the same binary; when the binary is called 'rustup' or
//! 'rustup.exe' rustup behaves like the rustup commandline
//! application; when it is called 'rustc' it behaves as a proxy to
//! 'rustc'.
//!
//! This scheme is further used to distingush the rustup installer,
//! called 'rustup-init' which is again just the rustup binary under a
//! different name.

#![recursion_limit = "1024"]

#[macro_use]
mod log;
mod common;
mod download_tracker;
mod errors;
mod help;
mod job;
mod markdown;
mod proxy_mode;
mod rustup_mode;
mod self_update;
mod setup_mode;
mod term2;

use crate::errors::*;
use rustup::env_var::RUST_RECURSION_COUNT_MAX;

use std::env;
use std::path::PathBuf;

use rs_tracing::*;

fn main() {
    if let Err(ref e) = run_rustup() {
        common::report_error(e);
        std::process::exit(1);
    }
}

fn run_rustup() -> Result<()> {
    if let Ok(dir) = env::var("RUSTUP_TRACE_DIR") {
        open_trace_file!(dir)?;
    }
    let result = run_rustup_inner();
    if let Ok(_) = env::var("RUSTUP_TRACE_DIR") {
        close_trace_file!();
    }
    result
}

fn run_rustup_inner() -> Result<()> {
    // Guard against infinite proxy recursion. This mostly happens due to
    // bugs in rustup.
    do_recursion_guard()?;

    // The name of arg0 determines how the program is going to behave
    let arg0 = match env::var("RUSTUP_FORCE_ARG0") {
        Ok(v) => Some(v),
        Err(_) => env::args().next(),
    }
    .map(PathBuf::from);
    let name = arg0
        .as_ref()
        .and_then(|a| a.file_stem())
        .and_then(std::ffi::OsStr::to_str);

    match name {
        Some("rustup") => rustup_mode::main(),
        Some(n) if n.starts_with("rustup-setup") || n.starts_with("rustup-init") => {
            // NB: The above check is only for the prefix of the file
            // name. Browsers rename duplicates to
            // e.g. rustup-setup(2), and this allows all variations
            // to work.
            setup_mode::main()
        }
        Some(n) if n.starts_with("rustup-gc-") => {
            // This is the final uninstallation stage on windows where
            // rustup deletes its own exe
            self_update::complete_windows_uninstall()
        }
        Some(_) => proxy_mode::main(),
        None => {
            // Weird case. No arg0, or it's unparsable.
            Err(ErrorKind::NoExeName.into())
        }
    }
}

fn do_recursion_guard() -> Result<()> {
    let recursion_count = env::var("RUST_RECURSION_COUNT")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(0);
    if recursion_count > RUST_RECURSION_COUNT_MAX {
        return Err(ErrorKind::InfiniteRecursion.into());
    }

    Ok(())
}
<commit_msg>Fix clippy: redundant_pattern_matching<commit_after>//! The main rustup commandline application
//!
//! The rustup binary is a chimera, changing its behavior based on the
//! name of the binary. This is used most prominently to enable
//! rustup's tool 'proxies' - that is, rustup itself and the rustup
//! proxies are the same binary; when the binary is called 'rustup' or
//! 'rustup.exe' rustup behaves like the rustup commandline
//! application; when it is called 'rustc' it behaves as a proxy to
//! 'rustc'.
//!
//! This scheme is further used to distingush the rustup installer,
//! called 'rustup-init' which is again just the rustup binary under a
//! different name.

#![recursion_limit = "1024"]

#[macro_use]
mod log;
mod common;
mod download_tracker;
mod errors;
mod help;
mod job;
mod markdown;
mod proxy_mode;
mod rustup_mode;
mod self_update;
mod setup_mode;
mod term2;

use crate::errors::*;
use rustup::env_var::RUST_RECURSION_COUNT_MAX;

use std::env;
use std::path::PathBuf;

use rs_tracing::*;

fn main() {
    if let Err(ref e) = run_rustup() {
        common::report_error(e);
        std::process::exit(1);
    }
}

fn run_rustup() -> Result<()> {
    if let Ok(dir) = env::var("RUSTUP_TRACE_DIR") {
        open_trace_file!(dir)?;
    }
    let result = run_rustup_inner();
    if env::var("RUSTUP_TRACE_DIR").is_ok() {
        close_trace_file!();
    }
    result
}

fn run_rustup_inner() -> Result<()> {
    // Guard against infinite proxy recursion. This mostly happens due to
    // bugs in rustup.
    do_recursion_guard()?;

    // The name of arg0 determines how the program is going to behave
    let arg0 = match env::var("RUSTUP_FORCE_ARG0") {
        Ok(v) => Some(v),
        Err(_) => env::args().next(),
    }
    .map(PathBuf::from);
    let name = arg0
        .as_ref()
        .and_then(|a| a.file_stem())
        .and_then(std::ffi::OsStr::to_str);

    match name {
        Some("rustup") => rustup_mode::main(),
        Some(n) if n.starts_with("rustup-setup") || n.starts_with("rustup-init") => {
            // NB: The above check is only for the prefix of the file
            // name. Browsers rename duplicates to
            // e.g. rustup-setup(2), and this allows all variations
            // to work.
            setup_mode::main()
        }
        Some(n) if n.starts_with("rustup-gc-") => {
            // This is the final uninstallation stage on windows where
            // rustup deletes its own exe
            self_update::complete_windows_uninstall()
        }
        Some(_) => proxy_mode::main(),
        None => {
            // Weird case. No arg0, or it's unparsable.
            Err(ErrorKind::NoExeName.into())
        }
    }
}

fn do_recursion_guard() -> Result<()> {
    let recursion_count = env::var("RUST_RECURSION_COUNT")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(0);
    if recursion_count > RUST_RECURSION_COUNT_MAX {
        return Err(ErrorKind::InfiniteRecursion.into());
    }

    Ok(())
}
<|endoftext|>


<commit_before><commit_msg>Clean up and reformat latency benchmark<commit_after><|endoftext|>


<commit_before>//! The oldest module, the data module stores all the data needed to collect.
use super::config;
use libp2p::PeerId;
use std::{
    cmp,                                     //max
    collections::hash_map::{Entry, HashMap}, // my main item uses a hash map
    fs::{self, DirEntry, Permissions},       // directory
    io,                                      // reading files
    os::linux::fs::MetadataExt,              //use std::os::windows::fs::MetadataExt;
    path::{Path, PathBuf},
}; // path, clear
use taglib;
use tree_magic;

#[allow(dead_code)]
/// # File info
/// All info on a audio files
/// (let's see how info we need,
/// its size vs necessary info)
struct FileInfo {
    path: PathBuf,
    size: u64,
    permissions: Permissions,
}

/// # Album information
/// General info
struct InfoAlbum {
    reference_path: Vec<FileInfo>,
}

#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Collection {
    /// This collection contains all data
    who: Worker,
    collection: HashMap<String, Box<InfoAlbum>>,
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    files: FilesStat,
    audio: Audio,
    threads: usize,
}

#[derive(Serialize, Deserialize, Debug)]
struct Audio {
    albums: u32,
    max_songs: usize,
}

type FileFn = dyn Fn(&mut Collection, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            collection: HashMap::new(),
            stats: Stats {
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                audio: Audio {
                    albums: 0,
                    max_songs: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(&mut self, dir: &Path, cb: &FileFn) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(&path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, &entry, &mut loop_file_stats).or_else(|io_error| {
                        warn!("{:?}", io_error);
                        Err(io_error)
                    })?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
            Some("audio") => {
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(&cb.path(), file_stats).or_else(|_| {
                            col.stats.files.faulty += 1;
                            file_stats.faulty += 1;
                            error!("ts: {:?}", filetype);
                            Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                        })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
            // FIXME: video in taglib holds also oga which are audio indeed ...
            Some("text") | Some("application") | Some("image") | Some("video") => Ok(()),
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(&mut self, cb: &Path, file_stats: &mut FilesStat) -> Result<(), ()> {
        taglib::File::new(cb.to_str().unwrap())
            .and_then(|file| {
                match file.tag() {
                    Ok(tag) => {
                        let artist = tag.artist().unwrap_or("unknown".to_string());

                        self.stats.files.analyzed += 1;
                        file_stats.analyzed += 1;

                        let path_buffer = cb.to_path_buf();

                        let metadata = fs::metadata(cb).unwrap();
                        let filesize = metadata.st_size();
                        let permissions = metadata.permissions();

                        let possible_entry = FileInfo {
                            path: path_buffer,
                            size: filesize,
                            permissions: permissions,
                        };

                        match self.collection.entry(String::from(artist)) {
                            Entry::Occupied(mut entry) => {
                                entry.get_mut().reference_path.push(possible_entry);

                                let this_albums_length = entry.get().reference_path.len();
                                self.stats.audio.max_songs =
                                    cmp::max(self.stats.audio.max_songs, this_albums_length);
                            }
                            Entry::Vacant(entry) => {
                                let this_album: InfoAlbum = InfoAlbum {
                                    reference_path: vec![possible_entry],
                                };
                                entry.insert(Box::new(this_album));
                                self.stats.audio.albums += 1;
                            }
                        }
                    }
                    Err(_) => {
                        self.stats.files.faulty += 1;
                        file_stats.faulty += 1;
                    }
                }
                Ok(())
            })
            .or_else(|e| {
                error!("{:?}:{:?}", e, cb);
                Err(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             pathes/threads       : {nr_pathes:>width$}\n\
             albums found         : {albums_found:>width$}\n\
             most songs per album : {max_p_album:>width$}\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            albums_found = self.stats.audio.albums,
            max_p_album = self.stats.audio.max_songs,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }
}

/// Just a test for a drop function ;-)
impl Drop for Collection {
    fn drop(&mut self) {
        println!(
            "Dropping/destroying collection from {}",
            self.who.peer_id.to_string().to_uppercase()
        );
    }
}
<commit_msg>[tui] fix graphics destroying print message when collecting is done but tui should be kept alive<commit_after>//! The oldest module, the data module stores all the data needed to collect.
use super::config;
use libp2p::PeerId;
use std::{
    cmp,                                     //max
    collections::hash_map::{Entry, HashMap}, // my main item uses a hash map
    fs::{self, DirEntry, Permissions},       // directory
    io,                                      // reading files
    os::linux::fs::MetadataExt,              //use std::os::windows::fs::MetadataExt;
    path::{Path, PathBuf},
}; // path, clear
use taglib;
use tree_magic;

#[allow(dead_code)]
/// # File info
/// All info on a audio files
/// (let's see how info we need,
/// its size vs necessary info)
struct FileInfo {
    path: PathBuf,
    size: u64,
    permissions: Permissions,
}

/// # Album information
/// General info
struct InfoAlbum {
    reference_path: Vec<FileInfo>,
}

#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Collection {
    /// This collection contains all data
    who: Worker,
    collection: HashMap<String, Box<InfoAlbum>>,
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    files: FilesStat,
    audio: Audio,
    threads: usize,
}

#[derive(Serialize, Deserialize, Debug)]
struct Audio {
    albums: u32,
    max_songs: usize,
}

type FileFn = dyn Fn(&mut Collection, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            collection: HashMap::new(),
            stats: Stats {
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                audio: Audio {
                    albums: 0,
                    max_songs: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(&mut self, dir: &Path, cb: &FileFn) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(&path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, &entry, &mut loop_file_stats).or_else(|io_error| {
                        warn!("{:?}", io_error);
                        Err(io_error)
                    })?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
            Some("audio") => {
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(&cb.path(), file_stats).or_else(|_| {
                            col.stats.files.faulty += 1;
                            file_stats.faulty += 1;
                            error!("ts: {:?}", filetype);
                            Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                        })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
            // FIXME: video in taglib holds also oga which are audio indeed ...
            Some("text") | Some("application") | Some("image") | Some("video") => Ok(()),
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(&mut self, cb: &Path, file_stats: &mut FilesStat) -> Result<(), ()> {
        taglib::File::new(cb.to_str().unwrap())
            .and_then(|file| {
                match file.tag() {
                    Ok(tag) => {
                        let artist = tag.artist().unwrap_or("unknown".to_string());

                        self.stats.files.analyzed += 1;
                        file_stats.analyzed += 1;

                        let path_buffer = cb.to_path_buf();

                        let metadata = fs::metadata(cb).unwrap();
                        let filesize = metadata.st_size();
                        let permissions = metadata.permissions();

                        let possible_entry = FileInfo {
                            path: path_buffer,
                            size: filesize,
                            permissions: permissions,
                        };

                        match self.collection.entry(String::from(artist)) {
                            Entry::Occupied(mut entry) => {
                                entry.get_mut().reference_path.push(possible_entry);

                                let this_albums_length = entry.get().reference_path.len();
                                self.stats.audio.max_songs =
                                    cmp::max(self.stats.audio.max_songs, this_albums_length);
                            }
                            Entry::Vacant(entry) => {
                                let this_album: InfoAlbum = InfoAlbum {
                                    reference_path: vec![possible_entry],
                                };
                                entry.insert(Box::new(this_album));
                                self.stats.audio.albums += 1;
                            }
                        }
                    }
                    Err(_) => {
                        self.stats.files.faulty += 1;
                        file_stats.faulty += 1;
                    }
                }
                Ok(())
            })
            .or_else(|e| {
                error!("{:?}:{:?}", e, cb);
                Err(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             pathes/threads       : {nr_pathes:>width$}\n\
             albums found         : {albums_found:>width$}\n\
             most songs per album : {max_p_album:>width$}\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            albums_found = self.stats.audio.albums,
            max_p_album = self.stats.audio.max_songs,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }
}
<|endoftext|>


<commit_before>
use std::marker::PhantomData;
use ndarray::{RcArray, Dimension};
use super::traits::*;

pub mod markers {
    pub struct EulerMarker {}
    pub struct HeunMarker {}
    pub struct RK4Marker {}
}

pub struct Explicit<F, Marker> {
    f: F,
    dt: f64,
    phantom: PhantomData<Marker>,
}

impl<F, Marker> Explicit<F, Marker> {
    pub fn new(f: F, dt: f64) -> Self {
        Explicit {
            f: f,
            dt: dt,
            phantom: PhantomData,
        }
    }
}

impl<F, Marker> TimeStep for Explicit<F, Marker> {
    fn get_dt(&self) -> f64 {
        self.dt
    }
    fn set_dt(&mut self, dt: f64) {
        self.dt = dt;
    }
}

macro_rules! impl_time_evolution {
    ( $($mut_:tt), * ) => {

impl<'a, A, D, F> TimeEvolution<A, D> for &'a $($mut_),* Explicit<F, markers::EulerMarker>
    where A: OdeScalar<f64>,
          D: Dimension,
          &'a $($mut_),* F: EOM<A, D>
{
    #[inline(always)]
    fn iterate(self, x: RcArray<A, D>) -> RcArray<A, D> {
        let fx = self.f.rhs(x.clone());
        x + fx * self.dt
    }
}

impl<'a, A, D, F> TimeEvolution<A, D> for &'a $($mut_),* Explicit<F, markers::HeunMarker>
    where A: OdeScalar<f64>,
          D: Dimension,
          for<'b> &'b $($mut_),* F: EOM<A, D>
{
    #[inline(always)]
    fn iterate(self, x: RcArray<A, D>) -> RcArray<A, D> {
        let k1 = self.f.rhs(x.clone()) * self.dt;
        let k2 = self.f.rhs(x.clone() + k1.clone()) * self.dt;
        x + (k1 + k2) * 0.5
    }
}

impl<'a, A, D, F> TimeEvolution<A, D> for &'a $($mut_),* Explicit<F, markers::RK4Marker>
    where A: OdeScalar<f64>,
          D: Dimension,
          for<'b> &'b $($mut_),* F: EOM<A, D>
{
    #[inline(always)]
    fn iterate(self, x: RcArray<A, D>) -> RcArray<A, D> {
        let dt_2 = 0.5 * self.dt;
        let dt_6 = self.dt / 6.0;
        let k1 = self.f.rhs(x.clone());
        let l1 = k1.clone() * dt_2 + &x;
        let k2 = self.f.rhs(l1);
        let l2 = k2.clone() * dt_2 + &x;
        let k3 = self.f.rhs(l2);
        let l3 = k3.clone() * self.dt + &x;
        let k4 = self.f.rhs(l3);
        x + (k1 + (k2 + k3) * 2.0 + k4) * dt_6
    }
}

}} // impl_time_evolution!

impl_time_evolution!();
impl_time_evolution!(mut);

pub fn euler<F>(f: F, dt: f64) -> Explicit<F, markers::EulerMarker> {
    Explicit::new(f, dt)
}

pub fn heun<F>(f: F, dt: f64) -> Explicit<F, markers::HeunMarker> {
    Explicit::new(f, dt)
}

pub fn rk4<F>(f: F, dt: f64) -> Explicit<F, markers::RK4Marker> {
    Explicit::new(f, dt)
}
<commit_msg>Use where for also in Euler<commit_after>
use std::marker::PhantomData;
use ndarray::{RcArray, Dimension};
use super::traits::*;

pub mod markers {
    pub struct EulerMarker {}
    pub struct HeunMarker {}
    pub struct RK4Marker {}
}

pub struct Explicit<F, Marker> {
    f: F,
    dt: f64,
    phantom: PhantomData<Marker>,
}

impl<F, Marker> Explicit<F, Marker> {
    pub fn new(f: F, dt: f64) -> Self {
        Explicit {
            f: f,
            dt: dt,
            phantom: PhantomData,
        }
    }
}

impl<F, Marker> TimeStep for Explicit<F, Marker> {
    fn get_dt(&self) -> f64 {
        self.dt
    }
    fn set_dt(&mut self, dt: f64) {
        self.dt = dt;
    }
}

macro_rules! impl_time_evolution {
    ( $($mut_:tt), * ) => {

impl<'a, A, D, F> TimeEvolution<A, D> for &'a $($mut_),* Explicit<F, markers::EulerMarker>
    where A: OdeScalar<f64>,
          D: Dimension,
          for<'b> &'b $($mut_),* F: EOM<A, D>
{
    #[inline(always)]
    fn iterate(self, x: RcArray<A, D>) -> RcArray<A, D> {
        let fx = self.f.rhs(x.clone());
        x + fx * self.dt
    }
}

impl<'a, A, D, F> TimeEvolution<A, D> for &'a $($mut_),* Explicit<F, markers::HeunMarker>
    where A: OdeScalar<f64>,
          D: Dimension,
          for<'b> &'b $($mut_),* F: EOM<A, D>
{
    #[inline(always)]
    fn iterate(self, x: RcArray<A, D>) -> RcArray<A, D> {
        let k1 = self.f.rhs(x.clone()) * self.dt;
        let k2 = self.f.rhs(x.clone() + k1.clone()) * self.dt;
        x + (k1 + k2) * 0.5
    }
}

impl<'a, A, D, F> TimeEvolution<A, D> for &'a $($mut_),* Explicit<F, markers::RK4Marker>
    where A: OdeScalar<f64>,
          D: Dimension,
          for<'b> &'b $($mut_),* F: EOM<A, D>
{
    #[inline(always)]
    fn iterate(self, x: RcArray<A, D>) -> RcArray<A, D> {
        let dt_2 = 0.5 * self.dt;
        let dt_6 = self.dt / 6.0;
        let k1 = self.f.rhs(x.clone());
        let l1 = k1.clone() * dt_2 + &x;
        let k2 = self.f.rhs(l1);
        let l2 = k2.clone() * dt_2 + &x;
        let k3 = self.f.rhs(l2);
        let l3 = k3.clone() * self.dt + &x;
        let k4 = self.f.rhs(l3);
        x + (k1 + (k2 + k3) * 2.0 + k4) * dt_6
    }
}

}} // impl_time_evolution!

impl_time_evolution!();
impl_time_evolution!(mut);

pub fn euler<F>(f: F, dt: f64) -> Explicit<F, markers::EulerMarker> {
    Explicit::new(f, dt)
}

pub fn heun<F>(f: F, dt: f64) -> Explicit<F, markers::HeunMarker> {
    Explicit::new(f, dt)
}

pub fn rk4<F>(f: F, dt: f64) -> Explicit<F, markers::RK4Marker> {
    Explicit::new(f, dt)
}
<|endoftext|>


<commit_before>use std::{mem, ptr};
use libc;

use c;
use low;

type FfiType_      = *mut low::ffi_type;
type FfiTypeArray_ = *mut FfiType_;

#[derive(Debug)]
pub struct FfiType(FfiType_);

#[derive(Debug)]
pub struct FfiTypeArray {
    ptr: FfiTypeArray_,
    len: usize,
}

/// Computes the length of a raw `FfiTypeArray_` by searching for the
/// null terminator.
unsafe fn ffi_type_array_len(mut array: FfiTypeArray_) -> usize {
    let mut current = array;
    let mut count   = 0;
    while !(*current).is_null() {
        count += 1;
    }
    count
}

/// Creates an empty `FfiTypeArray_` with null terminator.
unsafe fn ffi_type_array_create_empty(len: usize) -> FfiTypeArray_ {
    let array = libc::malloc((len + 1) * mem::size_of::<FfiType_>())
                    as FfiTypeArray_;
    *array.offset(len as isize) = ptr::null::<low::ffi_type>() as FfiType_;
    array
}

/// Creates a null-terminated array of FfiType_. Takes ownership of
/// the elements.
unsafe fn ffi_type_array_create(elements: Vec<FfiType>) -> FfiTypeArray_ {
    let size = elements.len();
    let new  = ffi_type_array_create_empty(size);
    for i in 0 .. size {
        *new.offset(i as isize) = elements[i].0;
    }

    for t in elements {
        mem::forget(t);
    }

    println!("ffi_type_array_create(...) = {:?}", new);

    new
}

unsafe fn ffi_type_struct_create_raw(elements: FfiTypeArray_) -> FfiType_ {
    let new = libc::malloc(mem::size_of::<low::ffi_type>()) as FfiType_;

    (*new).size      = 0;
    (*new).alignment = 0;
    (*new).type_     = c::ffi_type_enum::STRUCT as ::libc::c_ushort;
    (*new).elements  = elements;

    println!("ffi_type_struct_create_raw({:?}) = {:?}", elements, new);

    new
}

/// Creates a struct ffi_type with the given elements. Takes ownership
/// of the elements.
unsafe fn ffi_type_struct_create(elements: Vec<FfiType>) -> FfiType_ {
    println!("ffi_type_array_create({:?})", elements);
    ffi_type_struct_create_raw(ffi_type_array_create(elements))
}

unsafe fn ffi_type_array_clone(old: FfiTypeArray_) -> FfiTypeArray_ {
    let size = ffi_type_array_len(old);
    let new   = ffi_type_array_create_empty(size);

    for i in 0 .. size {
        *new.offset(i as isize) = ffi_type_clone(*old.offset(i as isize));
    }

    new
}

unsafe fn ffi_type_clone(old: FfiType_) -> FfiType_ {
    if (*old).type_ == c::ffi_type_enum::STRUCT as u16 {
        ffi_type_struct_create_raw(ffi_type_array_clone((*old).elements))
    } else {
        old
    }
}

/// Destroys an array of FfiType_ and all of its elements.
unsafe fn ffi_type_array_destroy(victim: FfiTypeArray_) {
    println!("ffi_type_array_destroy({:?})", victim);
    let mut current = victim;
    while !(*current).is_null() {
        ffi_type_destroy(*current);
        current = current.offset(1);
    }

    libc::free(victim as *mut libc::c_void);
}

/// Destroys an FfiType_ if it was dynamically allocated.
unsafe fn ffi_type_destroy(victim: FfiType_) {
    println!("ffi_type_destroy({:?})", victim);
    if (*victim).type_ == c::ffi_type_enum::STRUCT as u16 {
        ffi_type_array_destroy((*victim).elements);
        libc::free(victim as *mut libc::c_void);
    }
}

impl Drop for FfiType {
    fn drop(&mut self) {
        unsafe { ffi_type_destroy(self.0) }
    }
}

impl Drop for FfiTypeArray {
    fn drop(&mut self) {
        unsafe { ffi_type_array_destroy(self.ptr) }
    }
}

impl FfiType {
    pub fn void() -> Self {
        FfiType(unsafe { &mut low::ffi_type_void })
    }

    pub fn uint8() -> Self {
        FfiType(unsafe { &mut low::ffi_type_uint8 })
    }

    pub fn sint8() -> Self {
        FfiType(unsafe { &mut low::ffi_type_sint8 })
    }

    pub fn uint16() -> Self {
        FfiType(unsafe { &mut low::ffi_type_uint16 })
    }

    pub fn sint16() -> Self {
        FfiType(unsafe { &mut low::ffi_type_sint16 })
    }

    pub fn uint32() -> Self {
        FfiType(unsafe { &mut low::ffi_type_uint32 })
    }

    pub fn sint32() -> Self {
        FfiType(unsafe { &mut low::ffi_type_sint32 })
    }

    pub fn uint64() -> Self {
        FfiType(unsafe { &mut low::ffi_type_uint64 })
    }

    pub fn sint64() -> Self {
        FfiType(unsafe { &mut low::ffi_type_sint64 })
    }

    pub fn float() -> Self {
        FfiType(unsafe { &mut low::ffi_type_float })
    }

    pub fn double() -> Self {
        FfiType(unsafe { &mut low::ffi_type_double })
    }

    pub fn pointer() -> Self {
        FfiType(unsafe { &mut low::ffi_type_pointer })
    }

    pub fn longdouble() -> Self {
        FfiType(unsafe { &mut low::ffi_type_longdouble })
    }

    pub fn complex_float() -> Self {
        FfiType(unsafe { &mut low::ffi_type_complex_float })
    }

    pub fn complex_double() -> Self {
        FfiType(unsafe { &mut low::ffi_type_complex_double })
    }

    pub fn complex_longdouble() -> Self {
        FfiType(unsafe { &mut low::ffi_type_complex_longdouble })
    }

    pub fn structure(fields: Vec<FfiType>) -> Self {
        println!("FfiType::structure({:?})", fields);
        unsafe {
            FfiType(ffi_type_struct_create(fields))
        }
    }

    pub fn as_raw_ptr(&self) -> *mut low::ffi_type {
        self.0
    }
}

impl FfiTypeArray {
    pub fn new(types: Vec<FfiType>) -> Self {
        let len = types.len();
        unsafe {
            FfiTypeArray {
                ptr: ffi_type_array_create(types),
                len: len,
            }
        }
    }

    pub fn len(&self) -> usize {
        self.len
    }

    pub fn as_raw_ptr(&self) -> *mut *mut low::ffi_type {
        self.ptr
    }
}

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

    #[test]
    fn create_uint64() {
        FfiType::uint64();
    }

    #[test]
    fn create_struct() {
        FfiType::structure(vec![FfiType::sint64(),
                                FfiType::sint64(),
                                FfiType::uint64()]);
    }

}
<commit_msg>no longer storing len in FfiTypeArray<commit_after>//! Representations of C types and arrays thereof.

use std::{mem, ptr};
use libc;

use c;
use low;

type FfiType_      = *mut low::ffi_type;
type FfiTypeArray_ = *mut FfiType_;

#[derive(Debug)]
pub struct FfiType(FfiType_);

#[derive(Debug)]
pub struct FfiTypeArray(FfiTypeArray_);

/// Computes the length of a raw `FfiTypeArray_` by searching for the
/// null terminator.
unsafe fn ffi_type_array_len(mut array: FfiTypeArray_) -> usize {
    let mut current = array;
    let mut count   = 0;
    while !(*current).is_null() {
        count += 1;
    }
    count
}

/// Creates an empty `FfiTypeArray_` with null terminator.
unsafe fn ffi_type_array_create_empty(len: usize) -> FfiTypeArray_ {
    let array = libc::malloc((len + 1) * mem::size_of::<FfiType_>())
                    as FfiTypeArray_;
    *array.offset(len as isize) = ptr::null::<low::ffi_type>() as FfiType_;
    array
}

/// Creates a null-terminated array of FfiType_. Takes ownership of
/// the elements.
unsafe fn ffi_type_array_create(elements: Vec<FfiType>) -> FfiTypeArray_ {
    let size = elements.len();
    let new  = ffi_type_array_create_empty(size);
    for i in 0 .. size {
        *new.offset(i as isize) = elements[i].0;
    }

    for t in elements {
        mem::forget(t);
    }

    println!("ffi_type_array_create(...) = {:?}", new);

    new
}

/// Creates a struct type from a raw array of element types.
unsafe fn ffi_type_struct_create_raw(elements: FfiTypeArray_) -> FfiType_ {
    let new = libc::malloc(mem::size_of::<low::ffi_type>()) as FfiType_;

    (*new).size      = 0;
    (*new).alignment = 0;
    (*new).type_     = c::ffi_type_enum::STRUCT as ::libc::c_ushort;
    (*new).elements  = elements;

    println!("ffi_type_struct_create_raw({:?}) = {:?}", elements, new);

    new
}

/// Creates a struct ffi_type with the given elements. Takes ownership
/// of the elements.
unsafe fn ffi_type_struct_create(elements: Vec<FfiType>) -> FfiType_ {
    println!("ffi_type_array_create({:?})", elements);
    ffi_type_struct_create_raw(ffi_type_array_create(elements))
}

/// Makes a copy of a type array.
unsafe fn ffi_type_array_clone(old: FfiTypeArray_) -> FfiTypeArray_ {
    let size = ffi_type_array_len(old);
    let new  = ffi_type_array_create_empty(size);

    for i in 0 .. size {
        *new.offset(i as isize) = ffi_type_clone(*old.offset(i as isize));
    }

    new
}

/// Makes a copy of a type.
unsafe fn ffi_type_clone(old: FfiType_) -> FfiType_ {
    if (*old).type_ == c::ffi_type_enum::STRUCT as u16 {
        ffi_type_struct_create_raw(ffi_type_array_clone((*old).elements))
    } else {
        old
    }
}

/// Destroys an array of FfiType_ and all of its elements.
unsafe fn ffi_type_array_destroy(victim: FfiTypeArray_) {
    println!("ffi_type_array_destroy({:?})", victim);
    let mut current = victim;
    while !(*current).is_null() {
        ffi_type_destroy(*current);
        current = current.offset(1);
    }

    libc::free(victim as *mut libc::c_void);
}

/// Destroys an FfiType_ if it was dynamically allocated.
unsafe fn ffi_type_destroy(victim: FfiType_) {
    println!("ffi_type_destroy({:?})", victim);
    if (*victim).type_ == c::ffi_type_enum::STRUCT as u16 {
        ffi_type_array_destroy((*victim).elements);
        libc::free(victim as *mut libc::c_void);
    }
}

impl Drop for FfiType {
    fn drop(&mut self) {
        unsafe { ffi_type_destroy(self.0) }
    }
}

impl Drop for FfiTypeArray {
    fn drop(&mut self) {
        unsafe { ffi_type_array_destroy(self.0) }
    }
}

impl FfiType {
    pub fn void() -> Self {
        FfiType(unsafe { &mut low::ffi_type_void })
    }

    pub fn uint8() -> Self {
        FfiType(unsafe { &mut low::ffi_type_uint8 })
    }

    pub fn sint8() -> Self {
        FfiType(unsafe { &mut low::ffi_type_sint8 })
    }

    pub fn uint16() -> Self {
        FfiType(unsafe { &mut low::ffi_type_uint16 })
    }

    pub fn sint16() -> Self {
        FfiType(unsafe { &mut low::ffi_type_sint16 })
    }

    pub fn uint32() -> Self {
        FfiType(unsafe { &mut low::ffi_type_uint32 })
    }

    pub fn sint32() -> Self {
        FfiType(unsafe { &mut low::ffi_type_sint32 })
    }

    pub fn uint64() -> Self {
        FfiType(unsafe { &mut low::ffi_type_uint64 })
    }

    pub fn sint64() -> Self {
        FfiType(unsafe { &mut low::ffi_type_sint64 })
    }

    pub fn float() -> Self {
        FfiType(unsafe { &mut low::ffi_type_float })
    }

    pub fn double() -> Self {
        FfiType(unsafe { &mut low::ffi_type_double })
    }

    pub fn pointer() -> Self {
        FfiType(unsafe { &mut low::ffi_type_pointer })
    }

    pub fn longdouble() -> Self {
        FfiType(unsafe { &mut low::ffi_type_longdouble })
    }

    pub fn complex_float() -> Self {
        FfiType(unsafe { &mut low::ffi_type_complex_float })
    }

    pub fn complex_double() -> Self {
        FfiType(unsafe { &mut low::ffi_type_complex_double })
    }

    pub fn complex_longdouble() -> Self {
        FfiType(unsafe { &mut low::ffi_type_complex_longdouble })
    }

    pub fn structure(fields: Vec<FfiType>) -> Self {
        println!("FfiType::structure({:?})", fields);
        unsafe {
            FfiType(ffi_type_struct_create(fields))
        }
    }

    pub fn as_raw_ptr(&self) -> *mut low::ffi_type {
        self.0
    }
}

impl FfiTypeArray {
    pub fn new(elements: Vec<FfiType>) -> Self {
        unsafe { FfiTypeArray(ffi_type_array_create(elements)) }
    }

    pub fn len(&self) -> usize {
        unsafe { ffi_type_array_len(self.0) }
    }

    pub fn as_raw_ptr(&self) -> *mut *mut low::ffi_type {
        self.0
    }
}

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

    #[test]
    fn create_uint64() {
        FfiType::uint64();
    }

    #[test]
    fn create_struct() {
        FfiType::structure(vec![FfiType::sint64(),
                                FfiType::sint64(),
                                FfiType::uint64()]);
    }

}
<|endoftext|>