<commit_before>use log::info;
use parking_lot::Mutex;
use std::{path::*, sync::Arc};

pub mod block;
pub mod index;
pub mod store;
pub mod verify;

pub use self::{
    block::*,
    index::{IndexStatus, Indexer, WriteBatch},
    store::BlockStore,
    verify::TxErr,
};

use crate::{asset::Asset, constants::*, crypto::*, script::*, tx::*};

#[derive(Clone, Debug, PartialEq)]
pub struct Properties {
    pub height: u64,
    pub owner: Box<OwnerTx>,
    pub token_supply: Asset,
    pub network_fee: Asset,
}

#[derive(Clone, Debug, PartialEq)]
pub struct AddressInfo {
    pub net_fee: Asset,
    pub addr_fee: Asset,
    pub balance: Asset,
}

pub struct Blockchain {
    indexer: Arc<Indexer>,
    store: Mutex<BlockStore>,
}

impl Blockchain {
    ///
    /// Creates a new `Blockchain` with an associated indexer and backing
    /// storage is automatically created based on the given `path`.
    ///
    pub fn new(path: &Path) -> Self {
        let indexer = Arc::new(Indexer::new(&Path::join(path, "index")));
        let store = BlockStore::new(&Path::join(path, "blklog"), Arc::clone(&indexer));
        Blockchain {
            indexer,
            store: Mutex::new(store),
        }
    }

    pub fn indexer(&self) -> Arc<Indexer> {
        Arc::clone(&self.indexer)
    }

    pub fn index_status(&self) -> IndexStatus {
        self.indexer.index_status()
    }

    pub fn reindex(&self) {
        {
            let status = self.indexer.index_status();
            if status != IndexStatus::None {
                panic!("expected index status to be None, got: {:?}", status);
            }
        }
        let mut store = self.store.lock();
        store.reindex_blocks(|batch, block| {
            for tx in &block.transactions {
                Blockchain::index_tx(batch, &tx);
            }
            if block.height % 1000 == 0 {
                info!("Indexed block {}", block.height);
            }
        });

        info!("Rebuilding tx expiry index");
        let manager = index::TxManager::new(self.indexer());
        for height in (0..=self.get_chain_height()).rev() {
            let sys_time = crate::util::get_epoch_ms();
            let block = store.get(height).unwrap();
            // Test a huge timestamp gap to ensure all active txs are indexed
            if block.timestamp > sys_time - (TX_EXPIRY_TIME * 2) {
                for tx in &block.transactions {
                    let data = TxPrecompData::from_tx(tx);
                    manager.insert(data.txid(), data.tx().timestamp);
                }
            } else {
                break;
            }
        }

        info!("Reindexing complete");
    }

    pub fn get_properties(&self) -> Properties {
        Properties {
            height: self.get_chain_height(),
            owner: Box::new(self.get_owner()),
            token_supply: self.indexer.get_token_supply(),
            network_fee: self
                .get_network_fee()
                .expect("unexpected error retrieving network fee"),
        }
    }

    #[inline]
    pub fn get_owner(&self) -> OwnerTx {
        self.indexer
            .get_owner()
            .expect("Failed to retrieve owner from index")
    }

    #[inline]
    pub fn get_chain_height(&self) -> u64 {
        self.indexer.get_chain_height()
    }

    pub fn get_chain_head(&self) -> Arc<SignedBlock> {
        let store = self.store.lock();
        let height = store.get_chain_height();
        store.get(height).expect("Failed to get blockchain head")
    }

    pub fn get_block(&self, height: u64) -> Option<Arc<SignedBlock>> {
        let store = self.store.lock();
        store.get(height)
    }

    pub fn get_address_info(
        &self,
        addr: &ScriptHash,
        additional_txs: &[TxVariant],
    ) -> Option<AddressInfo> {
        let net_fee = self.get_network_fee()?;
        let addr_fee = self.get_address_fee(addr, additional_txs)?;
        let balance = self.get_balance(addr, additional_txs)?;
        Some(AddressInfo {
            net_fee,
            addr_fee,
            balance,
        })
    }

    pub fn get_address_fee(
        &self,
        addr: &ScriptHash,
        additional_txs: &[TxVariant],
    ) -> Option<Asset> {
        let mut tx_count = 1;
        let mut delta = 0;

        macro_rules! handle_tx_match {
            ($tx:expr) => {
                let has_match = match $tx {
                    TxVariant::OwnerTx(_) => false,
                    TxVariant::MintTx(_) => false,
                    TxVariant::RewardTx(_) => false,
                    TxVariant::TransferTx(tx) => &tx.from == addr,
                };
                if has_match {
                    tx_count += 1;
                    delta = 0;
                }
            };
        }

        for tx in additional_txs {
            handle_tx_match!(tx);
        }

        for i in (0..=self.get_chain_height()).rev() {
            delta += 1;
            let block = self.get_block(i).unwrap();
            for tx in &block.transactions {
                handle_tx_match!(tx);
            }
            if delta == FEE_RESET_WINDOW {
                break;
            }
        }

        GRAEL_FEE_MIN.mul(GRAEL_FEE_MULT.pow(tx_count as u16)?)
    }

    pub fn get_network_fee(&self) -> Option<Asset> {
        // The network fee adjusts every 5 blocks so that users have a bigger time
        // frame to confirm the fee they want to spend without suddenly changing.
        use crate::constants::*;
        let max_height = self.get_chain_height();
        let max_height = max_height - (max_height % 5);
        let min_height = if max_height > NETWORK_FEE_AVG_WINDOW {
            max_height - NETWORK_FEE_AVG_WINDOW
        } else {
            0
        };

        let mut tx_count: u64 = 1;
        for i in min_height..=max_height {
            tx_count += self.get_block(i).unwrap().transactions.len() as u64;
        }
        tx_count /= NETWORK_FEE_AVG_WINDOW;
        if tx_count > u64::from(u16::max_value()) {
            return None;
        }

        GRAEL_FEE_MIN.mul(GRAEL_FEE_NET_MULT.pow(tx_count as u16)?)
    }

    pub fn get_balance(&self, addr: &ScriptHash, additional_txs: &[TxVariant]) -> Option<Asset> {
        let mut bal = self.indexer.get_balance(addr).unwrap_or_default();
        for tx in additional_txs {
            match tx {
                TxVariant::OwnerTx(_) => {}
                TxVariant::MintTx(tx) => {
                    if &tx.to == addr {
                        bal = bal.add(tx.amount)?;
                    }
                }
                TxVariant::RewardTx(tx) => {
                    if &tx.to == addr {
                        bal = bal.add(tx.rewards)?;
                    }
                }
                TxVariant::TransferTx(tx) => {
                    if &tx.from == addr {
                        bal = bal.sub(tx.fee)?;
                        bal = bal.sub(tx.amount)?;
                    } else if &tx.to == addr {
                        bal = bal.add(tx.amount)?;
                    }
                }
            }
        }

        Some(bal)
    }

    pub fn insert_block(&self, block: SignedBlock) -> Result<(), verify::BlockErr> {
        static CONFIG: verify::Config = verify::Config {
            reject_reward: false,
        };
        self.verify_block(&block, &self.get_chain_head(), CONFIG)?;
        let mut batch = WriteBatch::new(Arc::clone(&self.indexer));
        for tx in &block.transactions {
            Self::index_tx(&mut batch, tx);
        }
        self.store.lock().insert(&mut batch, block);
        batch.commit();

        Ok(())
    }

    fn verify_block(
        &self,
        block: &SignedBlock,
        prev_block: &SignedBlock,
        config: verify::Config,
    ) -> Result<(), verify::BlockErr> {
        if prev_block.height + 1 != block.height {
            return Err(verify::BlockErr::InvalidBlockHeight);
        } else if !block.verify_tx_merkle_root() {
            return Err(verify::BlockErr::InvalidMerkleRoot);
        } else if !block.verify_previous_hash(prev_block) {
            return Err(verify::BlockErr::InvalidPrevHash);
        }

        let owner = self.get_owner();
        if !block.sig_pair.verify(block.calc_hash().as_ref()) {
            return Err(verify::BlockErr::InvalidHash);
        } else if block.sig_pair.pub_key != owner.minter {
            return Err(verify::BlockErr::InvalidSignature);
        }

        let len = block.transactions.len();
        for i in 0..len {
            let tx = &block.transactions[i];
            let txs = &block.transactions[0..i];
            if let Err(e) = self.verify_tx(&TxPrecompData::from_tx(tx), txs, config) {
                return Err(verify::BlockErr::Tx(e));
            }
        }

        Ok(())
    }

    pub fn verify_tx(
        &self,
        data: &TxPrecompData,
        additional_txs: &[TxVariant],
        config: verify::Config,
    ) -> Result<(), TxErr> {
        macro_rules! check_zero_fee {
            ($asset:expr) => {
                if $asset.amount != 0 {
                    return Err(TxErr::InvalidFeeAmount);
                }
            };
        }

        macro_rules! check_suf_bal {
            ($asset:expr) => {
                if $asset.amount < 0 {
                    return Err(TxErr::InsufficientBalance);
                }
            };
        }

        let tx = data.tx();

        if tx.signature_pairs.len() > MAX_TX_SIGNATURES {
            return Err(TxErr::TooManySignatures);
        } else if let Some(script) = tx.script() {
            if script.len() > MAX_SCRIPT_BYTE_SIZE {
                return Err(TxErr::TxTooLarge);
            }
        }

        match tx {
            TxVariant::OwnerTx(new_owner) => {
                check_zero_fee!(tx.fee);

                let owner = self.get_owner();
                if owner.wallet != (&new_owner.script).into() {
                    return Err(TxErr::ScriptHashMismatch);
                }

                let success = ScriptEngine::new(data, &new_owner.script)
                    .eval()
                    .map_err(TxErr::ScriptEval)?;
                if !success {
                    return Err(TxErr::ScriptRetFalse);
                }
            }
            TxVariant::MintTx(mint_tx) => {
                check_zero_fee!(tx.fee);

                let owner = self.get_owner();
                if owner.wallet != (&mint_tx.script).into() {
                    return Err(TxErr::ScriptHashMismatch);
                }
                let success = ScriptEngine::new(data, &mint_tx.script)
                    .eval()
                    .map_err(TxErr::ScriptEval)?;
                if !success {
                    return Err(TxErr::ScriptRetFalse);
                }

                // Sanity check to ensure too many new coins can't be minted
                self.get_balance(&mint_tx.to, additional_txs)
                    .ok_or(TxErr::Arithmetic)?
                    .add(mint_tx.amount)
                    .ok_or(TxErr::Arithmetic)?;

                self.indexer
                    .get_token_supply()
                    .add(mint_tx.amount)
                    .ok_or(TxErr::Arithmetic)?;
            }
            TxVariant::RewardTx(tx) => {
                if config.reject_reward {
                    return Err(TxErr::TxProhibited);
                }
                // Reward transactions are internally generated, thus should panic on failure
                if tx.fee.amount != 0 {
                    panic!("reward tx must have no fee");
                } else if !tx.signature_pairs.is_empty() {
                    panic!("reward tx must not be signed");
                } else if tx.timestamp != 0 {
                    panic!("reward tx must have a timestamp of 0");
                }
            }
            TxVariant::TransferTx(transfer) => {
                if transfer.memo.len() > MAX_MEMO_BYTE_SIZE {
                    return Err(TxErr::TxTooLarge);
                }
                let info = self
                    .get_address_info(&transfer.from, additional_txs)
                    .ok_or(TxErr::Arithmetic)?;
                let total_fee = info.net_fee.add(info.addr_fee).ok_or(TxErr::Arithmetic)?;
                if tx.fee < total_fee {
                    return Err(TxErr::InvalidFeeAmount);
                } else if transfer.from != (&transfer.script).into() {
                    return Err(TxErr::ScriptHashMismatch);
                }

                let success = ScriptEngine::new(data, &transfer.script)
                    .eval()
                    .map_err(TxErr::ScriptEval)?;
                if !success {
                    return Err(TxErr::ScriptRetFalse);
                }

                let bal = info
                    .balance
                    .sub(transfer.fee)
                    .ok_or(TxErr::Arithmetic)?
                    .sub(transfer.amount)
                    .ok_or(TxErr::Arithmetic)?;
                check_suf_bal!(bal);
            }
        }
        Ok(())
    }

    fn index_tx(batch: &mut WriteBatch, tx: &TxVariant) {
        match tx {
            TxVariant::OwnerTx(tx) => {
                batch.set_owner(tx.clone());
            }
            TxVariant::MintTx(tx) => {
                batch.add_token_supply(tx.amount);
                batch.add_bal(&tx.to, tx.amount);
            }
            TxVariant::RewardTx(tx) => {
                batch.add_bal(&tx.to, tx.rewards);
            }
            TxVariant::TransferTx(tx) => {
                batch.sub_bal(&tx.from, tx.fee.add(tx.amount).unwrap());
                batch.add_bal(&tx.to, tx.amount);
            }
        }
    }

    pub fn create_genesis_block(&self, minter_key: KeyPair) -> GenesisBlockInfo {
        let info = GenesisBlockInfo::new(minter_key);
        let timestamp = crate::util::get_epoch_ms();

        let owner_tx = OwnerTx {
            base: Tx {
                tx_type: TxType::OWNER,
                fee: Asset::default(),
                timestamp,
                signature_pairs: Vec::new(),
            },
            minter: info.minter_key.0.clone(),
            wallet: (&info.script).into(),
            script: Builder::new().push(OpFrame::False).build(),
        };

        let block = (Block {
            height: 0,
            previous_hash: Digest::from_slice(&[0u8; 32]).unwrap(),
            tx_merkle_root: Digest::from_slice(&[0u8; 32]).unwrap(),
            timestamp,
            transactions: vec![TxVariant::OwnerTx(owner_tx.clone())],
        })
        .sign(&info.minter_key);

        let mut batch = WriteBatch::new(Arc::clone(&self.indexer));
        self.store.lock().insert_genesis(&mut batch, block);
        batch.set_owner(owner_tx);
        batch.commit();
        self.indexer.set_index_status(IndexStatus::Complete);

        info
    }
}

pub struct GenesisBlockInfo {
    pub minter_key: KeyPair,
    pub wallet_keys: [KeyPair; 4],
    pub script: Script,
}

impl GenesisBlockInfo {
    pub fn new(minter_key: KeyPair) -> Self {
        let wallet_keys = [
            KeyPair::gen(),
            KeyPair::gen(),
            KeyPair::gen(),
            KeyPair::gen(),
        ];

        let script = Builder::new()
            .push(OpFrame::PubKey(wallet_keys[0].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[1].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[2].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[3].0.clone()))
            .push(OpFrame::OpCheckMultiSig(2, 4))
            .build();

        Self {
            minter_key,
            wallet_keys,
            script,
        }
    }
}
<commit_msg>Add total_fee API to AddressInfo<commit_after>use log::info;
use parking_lot::Mutex;
use std::{path::*, sync::Arc};

pub mod block;
pub mod index;
pub mod store;
pub mod verify;

pub use self::{
    block::*,
    index::{IndexStatus, Indexer, WriteBatch},
    store::BlockStore,
    verify::TxErr,
};

use crate::{asset::Asset, constants::*, crypto::*, script::*, tx::*};

#[derive(Clone, Debug, PartialEq)]
pub struct Properties {
    pub height: u64,
    pub owner: Box<OwnerTx>,
    pub token_supply: Asset,
    pub network_fee: Asset,
}

#[derive(Clone, Debug, PartialEq)]
pub struct AddressInfo {
    pub net_fee: Asset,
    pub addr_fee: Asset,
    pub balance: Asset,
}

impl AddressInfo {
    pub fn total_fee(&self) -> Option<Asset> {
        self.net_fee.add(self.addr_fee)
    }
}

pub struct Blockchain {
    indexer: Arc<Indexer>,
    store: Mutex<BlockStore>,
}

impl Blockchain {
    ///
    /// Creates a new `Blockchain` with an associated indexer and backing
    /// storage is automatically created based on the given `path`.
    ///
    pub fn new(path: &Path) -> Self {
        let indexer = Arc::new(Indexer::new(&Path::join(path, "index")));
        let store = BlockStore::new(&Path::join(path, "blklog"), Arc::clone(&indexer));
        Blockchain {
            indexer,
            store: Mutex::new(store),
        }
    }

    pub fn indexer(&self) -> Arc<Indexer> {
        Arc::clone(&self.indexer)
    }

    pub fn index_status(&self) -> IndexStatus {
        self.indexer.index_status()
    }

    pub fn reindex(&self) {
        {
            let status = self.indexer.index_status();
            if status != IndexStatus::None {
                panic!("expected index status to be None, got: {:?}", status);
            }
        }
        let mut store = self.store.lock();
        store.reindex_blocks(|batch, block| {
            for tx in &block.transactions {
                Blockchain::index_tx(batch, &tx);
            }
            if block.height % 1000 == 0 {
                info!("Indexed block {}", block.height);
            }
        });

        info!("Rebuilding tx expiry index");
        let manager = index::TxManager::new(self.indexer());
        for height in (0..=self.get_chain_height()).rev() {
            let sys_time = crate::util::get_epoch_ms();
            let block = store.get(height).unwrap();
            // Test a huge timestamp gap to ensure all active txs are indexed
            if block.timestamp > sys_time - (TX_EXPIRY_TIME * 2) {
                for tx in &block.transactions {
                    let data = TxPrecompData::from_tx(tx);
                    manager.insert(data.txid(), data.tx().timestamp);
                }
            } else {
                break;
            }
        }

        info!("Reindexing complete");
    }

    pub fn get_properties(&self) -> Properties {
        Properties {
            height: self.get_chain_height(),
            owner: Box::new(self.get_owner()),
            token_supply: self.indexer.get_token_supply(),
            network_fee: self
                .get_network_fee()
                .expect("unexpected error retrieving network fee"),
        }
    }

    #[inline]
    pub fn get_owner(&self) -> OwnerTx {
        self.indexer
            .get_owner()
            .expect("Failed to retrieve owner from index")
    }

    #[inline]
    pub fn get_chain_height(&self) -> u64 {
        self.indexer.get_chain_height()
    }

    pub fn get_chain_head(&self) -> Arc<SignedBlock> {
        let store = self.store.lock();
        let height = store.get_chain_height();
        store.get(height).expect("Failed to get blockchain head")
    }

    pub fn get_block(&self, height: u64) -> Option<Arc<SignedBlock>> {
        let store = self.store.lock();
        store.get(height)
    }

    pub fn get_address_info(
        &self,
        addr: &ScriptHash,
        additional_txs: &[TxVariant],
    ) -> Option<AddressInfo> {
        let net_fee = self.get_network_fee()?;
        let addr_fee = self.get_address_fee(addr, additional_txs)?;
        let balance = self.get_balance(addr, additional_txs)?;
        Some(AddressInfo {
            net_fee,
            addr_fee,
            balance,
        })
    }

    pub fn get_address_fee(
        &self,
        addr: &ScriptHash,
        additional_txs: &[TxVariant],
    ) -> Option<Asset> {
        let mut tx_count = 1;
        let mut delta = 0;

        macro_rules! handle_tx_match {
            ($tx:expr) => {
                let has_match = match $tx {
                    TxVariant::OwnerTx(_) => false,
                    TxVariant::MintTx(_) => false,
                    TxVariant::RewardTx(_) => false,
                    TxVariant::TransferTx(tx) => &tx.from == addr,
                };
                if has_match {
                    tx_count += 1;
                    delta = 0;
                }
            };
        }

        for tx in additional_txs {
            handle_tx_match!(tx);
        }

        for i in (0..=self.get_chain_height()).rev() {
            delta += 1;
            let block = self.get_block(i).unwrap();
            for tx in &block.transactions {
                handle_tx_match!(tx);
            }
            if delta == FEE_RESET_WINDOW {
                break;
            }
        }

        GRAEL_FEE_MIN.mul(GRAEL_FEE_MULT.pow(tx_count as u16)?)
    }

    pub fn get_network_fee(&self) -> Option<Asset> {
        // The network fee adjusts every 5 blocks so that users have a bigger time
        // frame to confirm the fee they want to spend without suddenly changing.
        use crate::constants::*;
        let max_height = self.get_chain_height();
        let max_height = max_height - (max_height % 5);
        let min_height = if max_height > NETWORK_FEE_AVG_WINDOW {
            max_height - NETWORK_FEE_AVG_WINDOW
        } else {
            0
        };

        let mut tx_count: u64 = 1;
        for i in min_height..=max_height {
            tx_count += self.get_block(i).unwrap().transactions.len() as u64;
        }
        tx_count /= NETWORK_FEE_AVG_WINDOW;
        if tx_count > u64::from(u16::max_value()) {
            return None;
        }

        GRAEL_FEE_MIN.mul(GRAEL_FEE_NET_MULT.pow(tx_count as u16)?)
    }

    pub fn get_balance(&self, addr: &ScriptHash, additional_txs: &[TxVariant]) -> Option<Asset> {
        let mut bal = self.indexer.get_balance(addr).unwrap_or_default();
        for tx in additional_txs {
            match tx {
                TxVariant::OwnerTx(_) => {}
                TxVariant::MintTx(tx) => {
                    if &tx.to == addr {
                        bal = bal.add(tx.amount)?;
                    }
                }
                TxVariant::RewardTx(tx) => {
                    if &tx.to == addr {
                        bal = bal.add(tx.rewards)?;
                    }
                }
                TxVariant::TransferTx(tx) => {
                    if &tx.from == addr {
                        bal = bal.sub(tx.fee)?;
                        bal = bal.sub(tx.amount)?;
                    } else if &tx.to == addr {
                        bal = bal.add(tx.amount)?;
                    }
                }
            }
        }

        Some(bal)
    }

    pub fn insert_block(&self, block: SignedBlock) -> Result<(), verify::BlockErr> {
        static CONFIG: verify::Config = verify::Config {
            reject_reward: false,
        };
        self.verify_block(&block, &self.get_chain_head(), CONFIG)?;
        let mut batch = WriteBatch::new(Arc::clone(&self.indexer));
        for tx in &block.transactions {
            Self::index_tx(&mut batch, tx);
        }
        self.store.lock().insert(&mut batch, block);
        batch.commit();

        Ok(())
    }

    fn verify_block(
        &self,
        block: &SignedBlock,
        prev_block: &SignedBlock,
        config: verify::Config,
    ) -> Result<(), verify::BlockErr> {
        if prev_block.height + 1 != block.height {
            return Err(verify::BlockErr::InvalidBlockHeight);
        } else if !block.verify_tx_merkle_root() {
            return Err(verify::BlockErr::InvalidMerkleRoot);
        } else if !block.verify_previous_hash(prev_block) {
            return Err(verify::BlockErr::InvalidPrevHash);
        }

        let owner = self.get_owner();
        if !block.sig_pair.verify(block.calc_hash().as_ref()) {
            return Err(verify::BlockErr::InvalidHash);
        } else if block.sig_pair.pub_key != owner.minter {
            return Err(verify::BlockErr::InvalidSignature);
        }

        let len = block.transactions.len();
        for i in 0..len {
            let tx = &block.transactions[i];
            let txs = &block.transactions[0..i];
            if let Err(e) = self.verify_tx(&TxPrecompData::from_tx(tx), txs, config) {
                return Err(verify::BlockErr::Tx(e));
            }
        }

        Ok(())
    }

    pub fn verify_tx(
        &self,
        data: &TxPrecompData,
        additional_txs: &[TxVariant],
        config: verify::Config,
    ) -> Result<(), TxErr> {
        macro_rules! check_zero_fee {
            ($asset:expr) => {
                if $asset.amount != 0 {
                    return Err(TxErr::InvalidFeeAmount);
                }
            };
        }

        macro_rules! check_suf_bal {
            ($asset:expr) => {
                if $asset.amount < 0 {
                    return Err(TxErr::InsufficientBalance);
                }
            };
        }

        let tx = data.tx();

        if tx.signature_pairs.len() > MAX_TX_SIGNATURES {
            return Err(TxErr::TooManySignatures);
        } else if let Some(script) = tx.script() {
            if script.len() > MAX_SCRIPT_BYTE_SIZE {
                return Err(TxErr::TxTooLarge);
            }
        }

        match tx {
            TxVariant::OwnerTx(new_owner) => {
                check_zero_fee!(tx.fee);

                let owner = self.get_owner();
                if owner.wallet != (&new_owner.script).into() {
                    return Err(TxErr::ScriptHashMismatch);
                }

                let success = ScriptEngine::new(data, &new_owner.script)
                    .eval()
                    .map_err(TxErr::ScriptEval)?;
                if !success {
                    return Err(TxErr::ScriptRetFalse);
                }
            }
            TxVariant::MintTx(mint_tx) => {
                check_zero_fee!(tx.fee);

                let owner = self.get_owner();
                if owner.wallet != (&mint_tx.script).into() {
                    return Err(TxErr::ScriptHashMismatch);
                }
                let success = ScriptEngine::new(data, &mint_tx.script)
                    .eval()
                    .map_err(TxErr::ScriptEval)?;
                if !success {
                    return Err(TxErr::ScriptRetFalse);
                }

                // Sanity check to ensure too many new coins can't be minted
                self.get_balance(&mint_tx.to, additional_txs)
                    .ok_or(TxErr::Arithmetic)?
                    .add(mint_tx.amount)
                    .ok_or(TxErr::Arithmetic)?;

                self.indexer
                    .get_token_supply()
                    .add(mint_tx.amount)
                    .ok_or(TxErr::Arithmetic)?;
            }
            TxVariant::RewardTx(tx) => {
                if config.reject_reward {
                    return Err(TxErr::TxProhibited);
                }
                // Reward transactions are internally generated, thus should panic on failure
                if tx.fee.amount != 0 {
                    panic!("reward tx must have no fee");
                } else if !tx.signature_pairs.is_empty() {
                    panic!("reward tx must not be signed");
                } else if tx.timestamp != 0 {
                    panic!("reward tx must have a timestamp of 0");
                }
            }
            TxVariant::TransferTx(transfer) => {
                if transfer.memo.len() > MAX_MEMO_BYTE_SIZE {
                    return Err(TxErr::TxTooLarge);
                }
                let info = self
                    .get_address_info(&transfer.from, additional_txs)
                    .ok_or(TxErr::Arithmetic)?;
                let total_fee = info.total_fee().ok_or(TxErr::Arithmetic)?;
                if tx.fee < total_fee {
                    return Err(TxErr::InvalidFeeAmount);
                } else if transfer.from != (&transfer.script).into() {
                    return Err(TxErr::ScriptHashMismatch);
                }

                let success = ScriptEngine::new(data, &transfer.script)
                    .eval()
                    .map_err(TxErr::ScriptEval)?;
                if !success {
                    return Err(TxErr::ScriptRetFalse);
                }

                let bal = info
                    .balance
                    .sub(transfer.fee)
                    .ok_or(TxErr::Arithmetic)?
                    .sub(transfer.amount)
                    .ok_or(TxErr::Arithmetic)?;
                check_suf_bal!(bal);
            }
        }
        Ok(())
    }

    fn index_tx(batch: &mut WriteBatch, tx: &TxVariant) {
        match tx {
            TxVariant::OwnerTx(tx) => {
                batch.set_owner(tx.clone());
            }
            TxVariant::MintTx(tx) => {
                batch.add_token_supply(tx.amount);
                batch.add_bal(&tx.to, tx.amount);
            }
            TxVariant::RewardTx(tx) => {
                batch.add_bal(&tx.to, tx.rewards);
            }
            TxVariant::TransferTx(tx) => {
                batch.sub_bal(&tx.from, tx.fee.add(tx.amount).unwrap());
                batch.add_bal(&tx.to, tx.amount);
            }
        }
    }

    pub fn create_genesis_block(&self, minter_key: KeyPair) -> GenesisBlockInfo {
        let info = GenesisBlockInfo::new(minter_key);
        let timestamp = crate::util::get_epoch_ms();

        let owner_tx = OwnerTx {
            base: Tx {
                tx_type: TxType::OWNER,
                fee: Asset::default(),
                timestamp,
                signature_pairs: Vec::new(),
            },
            minter: info.minter_key.0.clone(),
            wallet: (&info.script).into(),
            script: Builder::new().push(OpFrame::False).build(),
        };

        let block = (Block {
            height: 0,
            previous_hash: Digest::from_slice(&[0u8; 32]).unwrap(),
            tx_merkle_root: Digest::from_slice(&[0u8; 32]).unwrap(),
            timestamp,
            transactions: vec![TxVariant::OwnerTx(owner_tx.clone())],
        })
        .sign(&info.minter_key);

        let mut batch = WriteBatch::new(Arc::clone(&self.indexer));
        self.store.lock().insert_genesis(&mut batch, block);
        batch.set_owner(owner_tx);
        batch.commit();
        self.indexer.set_index_status(IndexStatus::Complete);

        info
    }
}

pub struct GenesisBlockInfo {
    pub minter_key: KeyPair,
    pub wallet_keys: [KeyPair; 4],
    pub script: Script,
}

impl GenesisBlockInfo {
    pub fn new(minter_key: KeyPair) -> Self {
        let wallet_keys = [
            KeyPair::gen(),
            KeyPair::gen(),
            KeyPair::gen(),
            KeyPair::gen(),
        ];

        let script = Builder::new()
            .push(OpFrame::PubKey(wallet_keys[0].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[1].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[2].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[3].0.clone()))
            .push(OpFrame::OpCheckMultiSig(2, 4))
            .build();

        Self {
            minter_key,
            wallet_keys,
            script,
        }
    }
}
<|endoftext|>


<commit_before>extern crate serde_json;
extern crate rmp_serde;

use std::cmp::Eq;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use super::zmq;
use errors::common::CommonError;

use services::ledger::merkletree::merkletree::MerkleTree;
use utils::json::{JsonDecodable, JsonEncodable};

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct NodeData {
    pub alias: String,
    pub client_ip: String,
    pub client_port: u32,
    pub node_ip: String,
    pub node_port: u32,
    pub services: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct GenTransaction {
    pub data: NodeData,
    pub dest: String,
    pub identifier: String,
    #[serde(rename = "txnId")]
    pub txn_id: Option<String>,
    #[serde(rename = "type")]
    pub txn_type: String,
}

impl JsonEncodable for GenTransaction {}

impl<'a> JsonDecodable<'a> for GenTransaction {}

impl GenTransaction {
    pub fn to_msg_pack(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
        rmp_serde::to_vec_named(self)
    }
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct LedgerStatus {
    pub txnSeqNo: usize,
    pub merkleRoot: String,
    pub ledgerId: u8,
    pub ppSeqNo: Option<String>,
    pub viewNo: Option<String>,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct ConsistencyProof {
    //TODO almost all fields Option<> or find better approach
    pub seqNoEnd: usize,
    pub seqNoStart: usize,
    pub ledgerId: usize,
    pub hashes: Vec<String>,
    pub oldMerkleRoot: String,
    pub newMerkleRoot: String,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct CatchupReq {
    pub ledgerId: usize,
    pub seqNoStart: usize,
    pub seqNoEnd: usize,
    pub catchupTill: usize,
}

impl<'a> JsonDecodable<'a> for CatchupReq {}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct CatchupRep {
    pub ledgerId: usize,
    pub consProof: Vec<String>,
    pub txns: HashMap<String, serde_json::Value>,
}

impl CatchupRep {
    pub fn min_tx(&self) -> Result<usize, CommonError> {
        let mut min = None;
        for (k, _) in self.txns.iter() {
            let val = k.parse::<usize>()
                .map_err(|err| CommonError::InvalidStructure(format!("{:?}", err)))?;
            match min {
                None => min = Some(val),
                Some(m) => if val < m { min = Some(val) }
            }
        }
        min.ok_or(CommonError::InvalidStructure(format!("Empty Map")))
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Reply {
    pub result: Response,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Response {
    pub req_id: u64,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PoolLedgerTxns {
    pub txn: Response,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SimpleRequest {
    pub req_id: u64,
}

impl JsonEncodable for SimpleRequest {}

impl<'a> JsonDecodable<'a> for SimpleRequest {}

#[serde(tag = "op")]
#[derive(Serialize, Deserialize, Debug)]
pub enum Message {
    #[serde(rename = "CONSISTENCY_PROOF")]
    ConsistencyProof(ConsistencyProof),
    #[serde(rename = "LEDGER_STATUS")]
    LedgerStatus(LedgerStatus),
    #[serde(rename = "CATCHUP_REQ")]
    CatchupReq(CatchupReq),
    #[serde(rename = "CATCHUP_REP")]
    CatchupRep(CatchupRep),
    #[serde(rename = "REQACK")]
    ReqACK(Response),
    #[serde(rename = "REQNACK")]
    ReqNACK(Response),
    #[serde(rename = "REPLY")]
    Reply(Reply),
    #[serde(rename = "REJECT")]
    Reject(Response),
    #[serde(rename = "POOL_LEDGER_TXNS")]
    PoolLedgerTxns(PoolLedgerTxns),
    Ping,
    Pong,
}

impl Message {
    pub fn from_raw_str(str: &str) -> Result<Message, serde_json::Error> {
        match str {
            "po" => Ok(Message::Pong),
            "pi" => Ok(Message::Ping),
            _ => Message::from_json(str),
        }
    }
}

impl JsonEncodable for Message {}

impl<'a> JsonDecodable<'a> for Message {}

#[derive(Serialize, Deserialize)]
pub struct PoolConfig {
    pub genesis_txn: String
}

impl JsonEncodable for PoolConfig {}

impl<'a> JsonDecodable<'a> for PoolConfig {}

impl PoolConfig {
    pub fn default_for_name(name: &str) -> PoolConfig {
        let mut txn = name.to_string();
        txn += ".txn";
        PoolConfig { genesis_txn: txn }
    }
}

pub struct RemoteNode {
    pub name: String,
    pub public_key: Vec<u8>,
    pub zaddr: String,
    pub zsock: Option<zmq::Socket>,
    pub is_blacklisted: bool,
}

pub struct CatchUpProcess {
    pub merkle_tree: MerkleTree,
    pub pending_reps: Vec<(CatchupRep, usize)>,
}

pub trait MinValue {
    fn get_min_index(&self) -> Result<usize, CommonError>;
}

impl MinValue for Vec<(CatchupRep, usize)> {
    fn get_min_index(&self) -> Result<usize, CommonError> {
        let mut res = None;
        for (index, &(ref catchup_rep, _)) in self.iter().enumerate() {
            match res {
                None => { res = Some((catchup_rep, index)); }
                Some((min_rep, i)) => if catchup_rep.min_tx()? < min_rep.min_tx()? {
                    res = Some((catchup_rep, index));
                }
            }
        }
        Ok(res.ok_or(CommonError::InvalidStructure("Element not Found".to_string()))?.1)
    }
}

#[derive(Debug)]
pub struct HashableValue {
    pub inner: serde_json::Value
}

impl Eq for HashableValue {}

impl Hash for HashableValue {
    fn hash<H: Hasher>(&self, state: &mut H) {
        serde_json::to_string(&self.inner).unwrap().hash(state); //TODO
    }
}

impl PartialEq for HashableValue {
    fn eq(&self, other: &HashableValue) -> bool {
        self.inner.eq(&other.inner)
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct CommandProcess {
    pub nack_cnt: usize,
    pub replies: HashMap<HashableValue, usize>,
    pub cmd_ids: Vec<i32>,
}

#[derive(Debug, PartialEq, Eq)]
pub enum ZMQLoopAction {
    RequestToSend(RequestToSend),
    MessageToProcess(MessageToProcess),
    Terminate(i32),
    Refresh(i32),
}

#[derive(Debug, PartialEq, Eq)]
pub struct RequestToSend {
    pub request: String,
    pub id: i32,
}

#[derive(Debug, PartialEq, Eq)]
pub struct MessageToProcess {
    pub message: String,
    pub node_idx: usize,
}<commit_msg>Deleted Clone for CatchupRep<commit_after>extern crate serde_json;
extern crate rmp_serde;

use std::cmp::Eq;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use super::zmq;
use errors::common::CommonError;

use services::ledger::merkletree::merkletree::MerkleTree;
use utils::json::{JsonDecodable, JsonEncodable};

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct NodeData {
    pub alias: String,
    pub client_ip: String,
    pub client_port: u32,
    pub node_ip: String,
    pub node_port: u32,
    pub services: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct GenTransaction {
    pub data: NodeData,
    pub dest: String,
    pub identifier: String,
    #[serde(rename = "txnId")]
    pub txn_id: Option<String>,
    #[serde(rename = "type")]
    pub txn_type: String,
}

impl JsonEncodable for GenTransaction {}

impl<'a> JsonDecodable<'a> for GenTransaction {}

impl GenTransaction {
    pub fn to_msg_pack(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
        rmp_serde::to_vec_named(self)
    }
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct LedgerStatus {
    pub txnSeqNo: usize,
    pub merkleRoot: String,
    pub ledgerId: u8,
    pub ppSeqNo: Option<String>,
    pub viewNo: Option<String>,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct ConsistencyProof {
    //TODO almost all fields Option<> or find better approach
    pub seqNoEnd: usize,
    pub seqNoStart: usize,
    pub ledgerId: usize,
    pub hashes: Vec<String>,
    pub oldMerkleRoot: String,
    pub newMerkleRoot: String,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct CatchupReq {
    pub ledgerId: usize,
    pub seqNoStart: usize,
    pub seqNoEnd: usize,
    pub catchupTill: usize,
}

impl<'a> JsonDecodable<'a> for CatchupReq {}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct CatchupRep {
    pub ledgerId: usize,
    pub consProof: Vec<String>,
    pub txns: HashMap<String, serde_json::Value>,
}

impl CatchupRep {
    pub fn min_tx(&self) -> Result<usize, CommonError> {
        let mut min = None;
        for (k, _) in self.txns.iter() {
            let val = k.parse::<usize>()
                .map_err(|err| CommonError::InvalidStructure(format!("{:?}", err)))?;
            match min {
                None => min = Some(val),
                Some(m) => if val < m { min = Some(val) }
            }
        }
        min.ok_or(CommonError::InvalidStructure(format!("Empty Map")))
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Reply {
    pub result: Response,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Response {
    pub req_id: u64,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PoolLedgerTxns {
    pub txn: Response,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SimpleRequest {
    pub req_id: u64,
}

impl JsonEncodable for SimpleRequest {}

impl<'a> JsonDecodable<'a> for SimpleRequest {}

#[serde(tag = "op")]
#[derive(Serialize, Deserialize, Debug)]
pub enum Message {
    #[serde(rename = "CONSISTENCY_PROOF")]
    ConsistencyProof(ConsistencyProof),
    #[serde(rename = "LEDGER_STATUS")]
    LedgerStatus(LedgerStatus),
    #[serde(rename = "CATCHUP_REQ")]
    CatchupReq(CatchupReq),
    #[serde(rename = "CATCHUP_REP")]
    CatchupRep(CatchupRep),
    #[serde(rename = "REQACK")]
    ReqACK(Response),
    #[serde(rename = "REQNACK")]
    ReqNACK(Response),
    #[serde(rename = "REPLY")]
    Reply(Reply),
    #[serde(rename = "REJECT")]
    Reject(Response),
    #[serde(rename = "POOL_LEDGER_TXNS")]
    PoolLedgerTxns(PoolLedgerTxns),
    Ping,
    Pong,
}

impl Message {
    pub fn from_raw_str(str: &str) -> Result<Message, serde_json::Error> {
        match str {
            "po" => Ok(Message::Pong),
            "pi" => Ok(Message::Ping),
            _ => Message::from_json(str),
        }
    }
}

impl JsonEncodable for Message {}

impl<'a> JsonDecodable<'a> for Message {}

#[derive(Serialize, Deserialize)]
pub struct PoolConfig {
    pub genesis_txn: String
}

impl JsonEncodable for PoolConfig {}

impl<'a> JsonDecodable<'a> for PoolConfig {}

impl PoolConfig {
    pub fn default_for_name(name: &str) -> PoolConfig {
        let mut txn = name.to_string();
        txn += ".txn";
        PoolConfig { genesis_txn: txn }
    }
}

pub struct RemoteNode {
    pub name: String,
    pub public_key: Vec<u8>,
    pub zaddr: String,
    pub zsock: Option<zmq::Socket>,
    pub is_blacklisted: bool,
}

pub struct CatchUpProcess {
    pub merkle_tree: MerkleTree,
    pub pending_reps: Vec<(CatchupRep, usize)>,
}

pub trait MinValue {
    fn get_min_index(&self) -> Result<usize, CommonError>;
}

impl MinValue for Vec<(CatchupRep, usize)> {
    fn get_min_index(&self) -> Result<usize, CommonError> {
        let mut res = None;
        for (index, &(ref catchup_rep, _)) in self.iter().enumerate() {
            match res {
                None => { res = Some((catchup_rep, index)); }
                Some((min_rep, i)) => if catchup_rep.min_tx()? < min_rep.min_tx()? {
                    res = Some((catchup_rep, index));
                }
            }
        }
        Ok(res.ok_or(CommonError::InvalidStructure("Element not Found".to_string()))?.1)
    }
}

#[derive(Debug)]
pub struct HashableValue {
    pub inner: serde_json::Value
}

impl Eq for HashableValue {}

impl Hash for HashableValue {
    fn hash<H: Hasher>(&self, state: &mut H) {
        serde_json::to_string(&self.inner).unwrap().hash(state); //TODO
    }
}

impl PartialEq for HashableValue {
    fn eq(&self, other: &HashableValue) -> bool {
        self.inner.eq(&other.inner)
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct CommandProcess {
    pub nack_cnt: usize,
    pub replies: HashMap<HashableValue, usize>,
    pub cmd_ids: Vec<i32>,
}

#[derive(Debug, PartialEq, Eq)]
pub enum ZMQLoopAction {
    RequestToSend(RequestToSend),
    MessageToProcess(MessageToProcess),
    Terminate(i32),
    Refresh(i32),
}

#[derive(Debug, PartialEq, Eq)]
pub struct RequestToSend {
    pub request: String,
    pub id: i32,
}

#[derive(Debug, PartialEq, Eq)]
pub struct MessageToProcess {
    pub message: String,
    pub node_idx: usize,
}<|endoftext|>


<commit_before>

/* -*- mode: rust; indent-tabs-mode: nil -*-
 * Implementation of 'fasta' benchmark from
 * Computer Language Benchmarks Game
 * http://shootout.alioth.debian.org/
 */
use std;
import vec;
import uint;
import int;
import str;
import io::writer_util;

fn LINE_LENGTH() -> uint { ret 60u; }

type myrandom = @{mut last: u32};

fn myrandom_next(r: myrandom, mx: u32) -> u32 {
    r.last = (r.last * 3877u32 + 29573u32) % 139968u32;
    mx * r.last / 139968u32
}

type aminoacids = {ch: char, prob: u32};

fn make_cumulative(aa: ~[aminoacids]) -> ~[aminoacids] {
    let mut cp: u32 = 0u32;
    let mut ans: ~[aminoacids] = ~[];
    for aa.each |a| { cp += a.prob; ans += ~[{ch: a.ch, prob: cp}]; }
    ret ans;
}

fn select_random(r: u32, genelist: ~[aminoacids]) -> char {
    if r < genelist[0].prob { ret genelist[0].ch; }
    fn bisect(v: ~[aminoacids], lo: uint, hi: uint, target: u32) -> char {
        if hi > lo + 1u {
            let mid: uint = lo + (hi - lo) / 2u;
            if target < v[mid].prob {
                ret bisect(v, lo, mid, target);
            } else { ret bisect(v, mid, hi, target); }
        } else { ret v[hi].ch; }
    }
    ret bisect(genelist, 0u, vec::len::<aminoacids>(genelist) - 1u, r);
}

fn make_random_fasta(wr: io::writer, id: str, desc: str, genelist: ~[aminoacids], n: int) {
    wr.write_line(">" + id + " " + desc);
    let rng = @{mut last: std::rand::rng().next()};
    let mut op: str = "";
    for uint::range(0u, n as uint) |_i| {
        str::push_char(op, select_random(myrandom_next(rng, 100u32),
                                         genelist));
        if str::len(op) >= LINE_LENGTH() {
            wr.write_line(op);
            op = "";
        }
    }
    if str::len(op) > 0u { wr.write_line(op); }
}

fn make_repeat_fasta(wr: io::writer, id: str, desc: str, s: str, n: int) unsafe {
    wr.write_line(">" + id + " " + desc);
    let mut op: str = "";
    let sl: uint = str::len(s);
    for uint::range(0u, n as uint) |i| {
        str::unsafe::push_byte(op, s[i % sl]);
        if str::len(op) >= LINE_LENGTH() {
            wr.write_line(op);
            op = "";
        }
    }
    if str::len(op) > 0u { wr.write_line(op); }
}

fn acid(ch: char, prob: u32) -> aminoacids { ret {ch: ch, prob: prob}; }

fn main(args: ~[str]) {
    let args = if os::getenv("RUST_BENCH").is_some() {
        // alioth tests k-nucleotide with this data at 25,000,000
        ~["", "5000000"]
    } else if args.len() <= 1u {
        ~["", "1000"]
    } else {
        args
    };

    let writer = if os::getenv("RUST_BENCH").is_some() {
        result::get(io::file_writer("./shootout-fasta.data", ~[io::truncate, io::create]))
    } else {
        io::stdout()
    };

    let n = int::from_str(args[1]).get();

    let iub: ~[aminoacids] =
        make_cumulative(~[acid('a', 27u32), acid('c', 12u32), acid('g', 12u32),
                         acid('t', 27u32), acid('B', 2u32), acid('D', 2u32),
                         acid('H', 2u32), acid('K', 2u32), acid('M', 2u32),
                         acid('N', 2u32), acid('R', 2u32), acid('S', 2u32),
                         acid('V', 2u32), acid('W', 2u32), acid('Y', 2u32)]);
    let homosapiens: ~[aminoacids] =
        make_cumulative(~[acid('a', 30u32), acid('c', 20u32), acid('g', 20u32),
                         acid('t', 30u32)]);
    let alu: str =
        "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" +
            "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" +
            "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" +
            "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" +
            "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" +
            "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" +
            "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";
    make_repeat_fasta(writer, "ONE", "Homo sapiens alu", alu, n * 2);
    make_random_fasta(writer, "TWO", "IUB ambiguity codes", iub, n * 3);
    make_random_fasta(writer, "THREE",
                      "Homo sapiens frequency", homosapiens, n * 5);
}
<commit_msg>test: Fix shootout-fasta<commit_after>

/* -*- mode: rust; indent-tabs-mode: nil -*-
 * Implementation of 'fasta' benchmark from
 * Computer Language Benchmarks Game
 * http://shootout.alioth.debian.org/
 */
use std;
import vec;
import uint;
import int;
import str;
import io::writer_util;

fn LINE_LENGTH() -> uint { ret 60u; }

type myrandom = @{mut last: u32};

fn myrandom_next(r: myrandom, mx: u32) -> u32 {
    r.last = (r.last * 3877u32 + 29573u32) % 139968u32;
    mx * r.last / 139968u32
}

type aminoacids = {ch: char, prob: u32};

fn make_cumulative(aa: ~[aminoacids]) -> ~[aminoacids] {
    let mut cp: u32 = 0u32;
    let mut ans: ~[aminoacids] = ~[];
    for aa.each |a| { cp += a.prob; ans += ~[{ch: a.ch, prob: cp}]; }
    ret ans;
}

fn select_random(r: u32, genelist: ~[aminoacids]) -> char {
    if r < genelist[0].prob { ret genelist[0].ch; }
    fn bisect(v: ~[aminoacids], lo: uint, hi: uint, target: u32) -> char {
        if hi > lo + 1u {
            let mid: uint = lo + (hi - lo) / 2u;
            if target < v[mid].prob {
                ret bisect(v, lo, mid, target);
            } else { ret bisect(v, mid, hi, target); }
        } else { ret v[hi].ch; }
    }
    ret bisect(genelist, 0u, vec::len::<aminoacids>(genelist) - 1u, r);
}

fn make_random_fasta(wr: io::writer, id: str, desc: str, genelist: ~[aminoacids], n: int) {
    wr.write_line(">" + id + " " + desc);
    let rng = @{mut last: rand::rng().next()};
    let mut op: str = "";
    for uint::range(0u, n as uint) |_i| {
        str::push_char(op, select_random(myrandom_next(rng, 100u32),
                                         genelist));
        if str::len(op) >= LINE_LENGTH() {
            wr.write_line(op);
            op = "";
        }
    }
    if str::len(op) > 0u { wr.write_line(op); }
}

fn make_repeat_fasta(wr: io::writer, id: str, desc: str, s: str, n: int) unsafe {
    wr.write_line(">" + id + " " + desc);
    let mut op: str = "";
    let sl: uint = str::len(s);
    for uint::range(0u, n as uint) |i| {
        str::unsafe::push_byte(op, s[i % sl]);
        if str::len(op) >= LINE_LENGTH() {
            wr.write_line(op);
            op = "";
        }
    }
    if str::len(op) > 0u { wr.write_line(op); }
}

fn acid(ch: char, prob: u32) -> aminoacids { ret {ch: ch, prob: prob}; }

fn main(args: ~[str]) {
    let args = if os::getenv("RUST_BENCH").is_some() {
        // alioth tests k-nucleotide with this data at 25,000,000
        ~["", "5000000"]
    } else if args.len() <= 1u {
        ~["", "1000"]
    } else {
        args
    };

    let writer = if os::getenv("RUST_BENCH").is_some() {
        result::get(io::file_writer("./shootout-fasta.data", ~[io::truncate, io::create]))
    } else {
        io::stdout()
    };

    let n = int::from_str(args[1]).get();

    let iub: ~[aminoacids] =
        make_cumulative(~[acid('a', 27u32), acid('c', 12u32), acid('g', 12u32),
                         acid('t', 27u32), acid('B', 2u32), acid('D', 2u32),
                         acid('H', 2u32), acid('K', 2u32), acid('M', 2u32),
                         acid('N', 2u32), acid('R', 2u32), acid('S', 2u32),
                         acid('V', 2u32), acid('W', 2u32), acid('Y', 2u32)]);
    let homosapiens: ~[aminoacids] =
        make_cumulative(~[acid('a', 30u32), acid('c', 20u32), acid('g', 20u32),
                         acid('t', 30u32)]);
    let alu: str =
        "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" +
            "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" +
            "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" +
            "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" +
            "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" +
            "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" +
            "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";
    make_repeat_fasta(writer, "ONE", "Homo sapiens alu", alu, n * 2);
    make_random_fasta(writer, "TWO", "IUB ambiguity codes", iub, n * 3);
    make_random_fasta(writer, "THREE",
                      "Homo sapiens frequency", homosapiens, n * 5);
}
<|endoftext|>


<commit_before>//! This module contains the `InterpCx` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.

use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::LayoutOf;

use super::{InterpCx, Machine};

/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
/// same type as the result.
#[inline]
fn binop_left_homogeneous(op: mir::BinOp) -> bool {
    use rustc_middle::mir::BinOp::*;
    match op {
        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true,
        Eq | Ne | Lt | Le | Gt | Ge => false,
    }
}
/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
/// same type as the LHS.
#[inline]
fn binop_right_homogeneous(op: mir::BinOp) -> bool {
    use rustc_middle::mir::BinOp::*;
    match op {
        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
        Offset | Shl | Shr => false,
    }
}

impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    pub fn run(&mut self) -> InterpResult<'tcx> {
        while self.step()? {}
        Ok(())
    }

    /// Returns `true` as long as there are more things to do.
    ///
    /// This is used by [priroda](https://github.com/oli-obk/priroda)
    ///
    /// This is marked `#inline(always)` to work around adverserial codegen when `opt-level = 3`
    #[inline(always)]
    pub fn step(&mut self) -> InterpResult<'tcx, bool> {
        if self.stack().is_empty() {
            return Ok(false);
        }

        let loc = match self.frame().loc {
            Ok(loc) => loc,
            Err(_) => {
                // We are unwinding and this fn has no cleanup code.
                // Just go on unwinding.
                trace!("unwinding: skipping frame");
                self.pop_stack_frame(/* unwinding */ true)?;
                return Ok(true);
            }
        };
        let basic_block = &self.body().basic_blocks()[loc.block];

        let old_frames = self.frame_idx();

        if let Some(stmt) = basic_block.statements.get(loc.statement_index) {
            assert_eq!(old_frames, self.frame_idx());
            self.statement(stmt)?;
            return Ok(true);
        }

        M::before_terminator(self)?;

        let terminator = basic_block.terminator();
        assert_eq!(old_frames, self.frame_idx());
        self.terminator(terminator)?;
        Ok(true)
    }

    /// Runs the interpretation logic for the given `mir::Statement` at the current frame and
    /// statement counter. This also moves the statement counter forward.
    pub fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> {
        info!("{:?}", stmt);

        use rustc_middle::mir::StatementKind::*;

        // Some statements (e.g., box) push new stack frames.
        // We have to record the stack frame number *before* executing the statement.
        let frame_idx = self.frame_idx();

        match &stmt.kind {
            Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?,

            SetDiscriminant { place, variant_index } => {
                let dest = self.eval_place(**place)?;
                self.write_discriminant(*variant_index, &dest)?;
            }

            // Mark locals as alive
            StorageLive(local) => {
                self.storage_live(*local)?;
            }

            // Mark locals as dead
            StorageDead(local) => {
                self.storage_dead(*local)?;
            }

            // No dynamic semantics attached to `FakeRead`; MIR
            // interpreter is solely intended for borrowck'ed code.
            FakeRead(..) => {}

            // Stacked Borrows.
            Retag(kind, place) => {
                let dest = self.eval_place(**place)?;
                M::retag(self, *kind, &dest)?;
            }

            // Call CopyNonOverlapping
            CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { src, dst, count }) => {
                let src = self.eval_operand(src, None)?;
                let dst = self.eval_operand(dst, None)?;
                let count = self.eval_operand(count, None)?;
                self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true)?;
            }

            // Statements we do not track.
            AscribeUserType(..) => {}

            // Currently, Miri discards Coverage statements. Coverage statements are only injected
            // via an optional compile time MIR pass and have no side effects. Since Coverage
            // statements don't exist at the source level, it is safe for Miri to ignore them, even
            // for undefined behavior (UB) checks.
            //
            // A coverage counter inside a const expression (for example, a counter injected in a
            // const function) is discarded when the const is evaluated at compile time. Whether
            // this should change, and/or how to implement a const eval counter, is a subject of the
            // following issue:
            //
            // FIXME(#73156): Handle source code coverage in const eval
            Coverage(..) => {}

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}

            LlvmInlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"),
        }

        self.stack_mut()[frame_idx].loc.as_mut().unwrap().statement_index += 1;
        Ok(())
    }

    /// Evaluate an assignment statement.
    ///
    /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
    /// type writes its results directly into the memory specified by the place.
    pub fn eval_rvalue_into_place(
        &mut self,
        rvalue: &mir::Rvalue<'tcx>,
        place: mir::Place<'tcx>,
    ) -> InterpResult<'tcx> {
        let dest = self.eval_place(place)?;

        use rustc_middle::mir::Rvalue::*;
        match *rvalue {
            ThreadLocalRef(did) => {
                let ptr = M::thread_local_static_base_pointer(self, did)?;
                self.write_pointer(ptr, &dest)?;
            }

            Use(ref operand) => {
                // Avoid recomputing the layout
                let op = self.eval_operand(operand, Some(dest.layout))?;
                self.copy_op(&op, &dest)?;
            }

            BinaryOp(bin_op, box (ref left, ref right)) => {
                let layout = binop_left_homogeneous(bin_op).then_some(dest.layout);
                let left = self.read_immediate(&self.eval_operand(left, layout)?)?;
                let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
                let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
                self.binop_ignore_overflow(bin_op, &left, &right, &dest)?;
            }

            CheckedBinaryOp(bin_op, box (ref left, ref right)) => {
                // Due to the extra boolean in the result, we can never reuse the `dest.layout`.
                let left = self.read_immediate(&self.eval_operand(left, None)?)?;
                let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
                let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
                self.binop_with_overflow(bin_op, &left, &right, &dest)?;
            }

            UnaryOp(un_op, ref operand) => {
                // The operand always has the same type as the result.
                let val = self.read_immediate(&self.eval_operand(operand, Some(dest.layout))?)?;
                let val = self.unary_op(un_op, &val)?;
                assert_eq!(val.layout, dest.layout, "layout mismatch for result of {:?}", un_op);
                self.write_immediate(*val, &dest)?;
            }

            Aggregate(ref kind, ref operands) => {
                let (dest, active_field_index) = match **kind {
                    mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
                        self.write_discriminant(variant_index, &dest)?;
                        if adt_def.is_enum() {
                            (self.place_downcast(&dest, variant_index)?, active_field_index)
                        } else {
                            (dest, active_field_index)
                        }
                    }
                    _ => (dest, None),
                };

                for (i, operand) in operands.iter().enumerate() {
                    let op = self.eval_operand(operand, None)?;
                    // Ignore zero-sized fields.
                    if !op.layout.is_zst() {
                        let field_index = active_field_index.unwrap_or(i);
                        let field_dest = self.place_field(&dest, field_index)?;
                        self.copy_op(&op, &field_dest)?;
                    }
                }
            }

            Repeat(ref operand, _) => {
                let src = self.eval_operand(operand, None)?;
                assert!(!src.layout.is_unsized());
                let dest = self.force_allocation(&dest)?;
                let length = dest.len(self)?;

                if length == 0 {
                    // Nothing to copy... but let's still make sure that `dest` as a place is valid.
                    self.get_alloc_mut(&dest)?;
                } else {
                    // Write the src to the first element.
                    let first = self.mplace_field(&dest, 0)?;
                    self.copy_op(&src, &first.into())?;

                    // This is performance-sensitive code for big static/const arrays! So we
                    // avoid writing each operand individually and instead just make many copies
                    // of the first element.
                    let elem_size = first.layout.size;
                    let first_ptr = first.ptr;
                    let rest_ptr = first_ptr.offset(elem_size, self)?;
                    self.memory.copy_repeatedly(
                        first_ptr,
                        first.align,
                        rest_ptr,
                        first.align,
                        elem_size,
                        length - 1,
                        /*nonoverlapping:*/ true,
                    )?;
                }
            }

            Len(place) => {
                // FIXME(CTFE): don't allow computing the length of arrays in const eval
                let src = self.eval_place(place)?;
                let mplace = self.force_allocation(&src)?;
                let len = mplace.len(self)?;
                self.write_scalar(Scalar::from_machine_usize(len, self), &dest)?;
            }

            AddressOf(_, place) | Ref(_, _, place) => {
                let src = self.eval_place(place)?;
                let place = self.force_allocation(&src)?;
                self.write_immediate(place.to_ref(self), &dest)?;
            }

            NullaryOp(mir::NullOp::Box, _) => {
                M::box_alloc(self, &dest)?;
            }

            NullaryOp(null_op, ty) => {
                let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty);
                let layout = self.layout_of(ty)?;
                if layout.is_unsized() {
                    // FIXME: This should be a span_bug (#80742)
                    self.tcx.sess.delay_span_bug(
                        self.frame().current_span(),
                        &format!("Nullary MIR operator called for unsized type {}", ty),
                    );
                    throw_inval!(SizeOfUnsizedType(ty));
                }
                let val = match null_op {
                    mir::NullOp::SizeOf => layout.size.bytes(),
                    mir::NullOp::AlignOf => layout.align.abi.bytes(),
                    mir::NullOp::Box => unreachable!(),
                };
                self.write_scalar(Scalar::from_machine_usize(val, self), &dest)?;
            }

            ShallowInitBox(ref operand, _) => {
                let src = self.eval_operand(operand, None)?;
                let v = self.read_immediate(&src)?;
                self.write_immediate(*v, &dest)?;
            }

            Cast(cast_kind, ref operand, cast_ty) => {
                let src = self.eval_operand(operand, None)?;
                let cast_ty = self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty);
                self.cast(&src, cast_kind, cast_ty, &dest)?;
            }

            Discriminant(place) => {
                let op = self.eval_place_to_op(place, None)?;
                let discr_val = self.read_discriminant(&op)?.0;
                self.write_scalar(discr_val, &dest)?;
            }
        }

        trace!("{:?}", self.dump_place(*dest));

        Ok(())
    }

    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> {
        info!("{:?}", terminator.kind);

        self.eval_terminator(terminator)?;
        if !self.stack().is_empty() {
            if let Ok(loc) = self.frame().loc {
                info!("// executing {:?}", loc.block);
            }
        }
        Ok(())
    }
}
<commit_msg>Rollup merge of #89370 - RalfJung:ctfe-aggregate-rvalue, r=oli-obk<commit_after>//! This module contains the `InterpCx` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.

use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::LayoutOf;

use super::{InterpCx, Machine};

/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
/// same type as the result.
#[inline]
fn binop_left_homogeneous(op: mir::BinOp) -> bool {
    use rustc_middle::mir::BinOp::*;
    match op {
        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true,
        Eq | Ne | Lt | Le | Gt | Ge => false,
    }
}
/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
/// same type as the LHS.
#[inline]
fn binop_right_homogeneous(op: mir::BinOp) -> bool {
    use rustc_middle::mir::BinOp::*;
    match op {
        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
        Offset | Shl | Shr => false,
    }
}

impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
    pub fn run(&mut self) -> InterpResult<'tcx> {
        while self.step()? {}
        Ok(())
    }

    /// Returns `true` as long as there are more things to do.
    ///
    /// This is used by [priroda](https://github.com/oli-obk/priroda)
    ///
    /// This is marked `#inline(always)` to work around adverserial codegen when `opt-level = 3`
    #[inline(always)]
    pub fn step(&mut self) -> InterpResult<'tcx, bool> {
        if self.stack().is_empty() {
            return Ok(false);
        }

        let loc = match self.frame().loc {
            Ok(loc) => loc,
            Err(_) => {
                // We are unwinding and this fn has no cleanup code.
                // Just go on unwinding.
                trace!("unwinding: skipping frame");
                self.pop_stack_frame(/* unwinding */ true)?;
                return Ok(true);
            }
        };
        let basic_block = &self.body().basic_blocks()[loc.block];

        let old_frames = self.frame_idx();

        if let Some(stmt) = basic_block.statements.get(loc.statement_index) {
            assert_eq!(old_frames, self.frame_idx());
            self.statement(stmt)?;
            return Ok(true);
        }

        M::before_terminator(self)?;

        let terminator = basic_block.terminator();
        assert_eq!(old_frames, self.frame_idx());
        self.terminator(terminator)?;
        Ok(true)
    }

    /// Runs the interpretation logic for the given `mir::Statement` at the current frame and
    /// statement counter. This also moves the statement counter forward.
    pub fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> {
        info!("{:?}", stmt);

        use rustc_middle::mir::StatementKind::*;

        // Some statements (e.g., box) push new stack frames.
        // We have to record the stack frame number *before* executing the statement.
        let frame_idx = self.frame_idx();

        match &stmt.kind {
            Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?,

            SetDiscriminant { place, variant_index } => {
                let dest = self.eval_place(**place)?;
                self.write_discriminant(*variant_index, &dest)?;
            }

            // Mark locals as alive
            StorageLive(local) => {
                self.storage_live(*local)?;
            }

            // Mark locals as dead
            StorageDead(local) => {
                self.storage_dead(*local)?;
            }

            // No dynamic semantics attached to `FakeRead`; MIR
            // interpreter is solely intended for borrowck'ed code.
            FakeRead(..) => {}

            // Stacked Borrows.
            Retag(kind, place) => {
                let dest = self.eval_place(**place)?;
                M::retag(self, *kind, &dest)?;
            }

            // Call CopyNonOverlapping
            CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { src, dst, count }) => {
                let src = self.eval_operand(src, None)?;
                let dst = self.eval_operand(dst, None)?;
                let count = self.eval_operand(count, None)?;
                self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true)?;
            }

            // Statements we do not track.
            AscribeUserType(..) => {}

            // Currently, Miri discards Coverage statements. Coverage statements are only injected
            // via an optional compile time MIR pass and have no side effects. Since Coverage
            // statements don't exist at the source level, it is safe for Miri to ignore them, even
            // for undefined behavior (UB) checks.
            //
            // A coverage counter inside a const expression (for example, a counter injected in a
            // const function) is discarded when the const is evaluated at compile time. Whether
            // this should change, and/or how to implement a const eval counter, is a subject of the
            // following issue:
            //
            // FIXME(#73156): Handle source code coverage in const eval
            Coverage(..) => {}

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}

            LlvmInlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"),
        }

        self.stack_mut()[frame_idx].loc.as_mut().unwrap().statement_index += 1;
        Ok(())
    }

    /// Evaluate an assignment statement.
    ///
    /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
    /// type writes its results directly into the memory specified by the place.
    pub fn eval_rvalue_into_place(
        &mut self,
        rvalue: &mir::Rvalue<'tcx>,
        place: mir::Place<'tcx>,
    ) -> InterpResult<'tcx> {
        let dest = self.eval_place(place)?;

        use rustc_middle::mir::Rvalue::*;
        match *rvalue {
            ThreadLocalRef(did) => {
                let ptr = M::thread_local_static_base_pointer(self, did)?;
                self.write_pointer(ptr, &dest)?;
            }

            Use(ref operand) => {
                // Avoid recomputing the layout
                let op = self.eval_operand(operand, Some(dest.layout))?;
                self.copy_op(&op, &dest)?;
            }

            BinaryOp(bin_op, box (ref left, ref right)) => {
                let layout = binop_left_homogeneous(bin_op).then_some(dest.layout);
                let left = self.read_immediate(&self.eval_operand(left, layout)?)?;
                let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
                let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
                self.binop_ignore_overflow(bin_op, &left, &right, &dest)?;
            }

            CheckedBinaryOp(bin_op, box (ref left, ref right)) => {
                // Due to the extra boolean in the result, we can never reuse the `dest.layout`.
                let left = self.read_immediate(&self.eval_operand(left, None)?)?;
                let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
                let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
                self.binop_with_overflow(bin_op, &left, &right, &dest)?;
            }

            UnaryOp(un_op, ref operand) => {
                // The operand always has the same type as the result.
                let val = self.read_immediate(&self.eval_operand(operand, Some(dest.layout))?)?;
                let val = self.unary_op(un_op, &val)?;
                assert_eq!(val.layout, dest.layout, "layout mismatch for result of {:?}", un_op);
                self.write_immediate(*val, &dest)?;
            }

            Aggregate(ref kind, ref operands) => {
                // active_field_index is for union initialization.
                let (dest, active_field_index) = match **kind {
                    mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
                        self.write_discriminant(variant_index, &dest)?;
                        if adt_def.is_enum() {
                            assert!(active_field_index.is_none());
                            (self.place_downcast(&dest, variant_index)?, None)
                        } else {
                            if active_field_index.is_some() {
                                assert_eq!(operands.len(), 1);
                            }
                            (dest, active_field_index)
                        }
                    }
                    _ => (dest, None),
                };

                for (i, operand) in operands.iter().enumerate() {
                    let op = self.eval_operand(operand, None)?;
                    let field_index = active_field_index.unwrap_or(i);
                    let field_dest = self.place_field(&dest, field_index)?;
                    self.copy_op(&op, &field_dest)?;
                }
            }

            Repeat(ref operand, _) => {
                let src = self.eval_operand(operand, None)?;
                assert!(!src.layout.is_unsized());
                let dest = self.force_allocation(&dest)?;
                let length = dest.len(self)?;

                if length == 0 {
                    // Nothing to copy... but let's still make sure that `dest` as a place is valid.
                    self.get_alloc_mut(&dest)?;
                } else {
                    // Write the src to the first element.
                    let first = self.mplace_field(&dest, 0)?;
                    self.copy_op(&src, &first.into())?;

                    // This is performance-sensitive code for big static/const arrays! So we
                    // avoid writing each operand individually and instead just make many copies
                    // of the first element.
                    let elem_size = first.layout.size;
                    let first_ptr = first.ptr;
                    let rest_ptr = first_ptr.offset(elem_size, self)?;
                    self.memory.copy_repeatedly(
                        first_ptr,
                        first.align,
                        rest_ptr,
                        first.align,
                        elem_size,
                        length - 1,
                        /*nonoverlapping:*/ true,
                    )?;
                }
            }

            Len(place) => {
                let src = self.eval_place(place)?;
                let mplace = self.force_allocation(&src)?;
                let len = mplace.len(self)?;
                self.write_scalar(Scalar::from_machine_usize(len, self), &dest)?;
            }

            AddressOf(_, place) | Ref(_, _, place) => {
                let src = self.eval_place(place)?;
                let place = self.force_allocation(&src)?;
                self.write_immediate(place.to_ref(self), &dest)?;
            }

            NullaryOp(mir::NullOp::Box, _) => {
                M::box_alloc(self, &dest)?;
            }

            NullaryOp(null_op, ty) => {
                let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty);
                let layout = self.layout_of(ty)?;
                if layout.is_unsized() {
                    // FIXME: This should be a span_bug (#80742)
                    self.tcx.sess.delay_span_bug(
                        self.frame().current_span(),
                        &format!("Nullary MIR operator called for unsized type {}", ty),
                    );
                    throw_inval!(SizeOfUnsizedType(ty));
                }
                let val = match null_op {
                    mir::NullOp::SizeOf => layout.size.bytes(),
                    mir::NullOp::AlignOf => layout.align.abi.bytes(),
                    mir::NullOp::Box => unreachable!(),
                };
                self.write_scalar(Scalar::from_machine_usize(val, self), &dest)?;
            }

            ShallowInitBox(ref operand, _) => {
                let src = self.eval_operand(operand, None)?;
                let v = self.read_immediate(&src)?;
                self.write_immediate(*v, &dest)?;
            }

            Cast(cast_kind, ref operand, cast_ty) => {
                let src = self.eval_operand(operand, None)?;
                let cast_ty = self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty);
                self.cast(&src, cast_kind, cast_ty, &dest)?;
            }

            Discriminant(place) => {
                let op = self.eval_place_to_op(place, None)?;
                let discr_val = self.read_discriminant(&op)?.0;
                self.write_scalar(discr_val, &dest)?;
            }
        }

        trace!("{:?}", self.dump_place(*dest));

        Ok(())
    }

    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> {
        info!("{:?}", terminator.kind);

        self.eval_terminator(terminator)?;
        if !self.stack().is_empty() {
            if let Ok(loc) = self.frame().loc {
                info!("// executing {:?}", loc.block);
            }
        }
        Ok(())
    }
}
<|endoftext|>