<commit_before>extern crate rs_nes;

use rs_nes::cpu::*;

/// ## Sign and zero flag tests
///
/// These tests check the presence of the sign and zero flag.

#[test]
fn adc_flags_sign_and_zero_1() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x0;
  const ROP: u8 = 0x0;

  // decimal values sanity check
  assert_eq!(0, LOP);
  assert_eq!(0, ROP);
  assert_eq!(0, LOP as i8);
  assert_eq!(0, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_ZERO));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_SIGN));
  assert_eq!(0x0, cpu.registers.acc);

  // decimal values sannity check
  assert_eq!(0, cpu.registers.acc);
  assert_eq!(0, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_sign_and_zero_2() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x0;
  const ROP: u8 = 0x1;

  // decimal values sanity check
  assert_eq!(0, LOP);
  assert_eq!(1, ROP);
  assert_eq!(0, LOP as i8);
  assert_eq!(1, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_ZERO));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_SIGN));
  assert_eq!(0x1, cpu.registers.acc);

  // decimal values sannity check
  assert_eq!(1, cpu.registers.acc);
  assert_eq!(1, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_sign_and_zero_3() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x0;
  const ROP: u8 = 0xff;;

  // decimal values sanity check
  assert_eq!(0, LOP);
  assert_eq!(255, ROP);
  assert_eq!(0, LOP as i8);
  assert_eq!(-1, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_ZERO));
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_SIGN));
  assert_eq!(0xff, cpu.registers.acc);

  // decimal values sannity check
  assert_eq!(255, cpu.registers.acc);
  assert_eq!(-1, cpu.registers.acc as i8);
}

/// ## Carry and overflow flag tests
///
/// The following tests check all permutations of the
/// 6th and 7th bits of both operands, asserting that
/// the overflow and carry bit is set appROPriately.
///
/// A truth table for these tests can be found here:
/// http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html

#[test]
fn adc_flags_carry_and_overflow_1() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x50;
  const ROP: u8 = 0x10;

  // decimal values sanity check
  assert_eq!(80, LOP);
  assert_eq!(16, ROP);
  assert_eq!(80, LOP as i8);
  assert_eq!(16, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0x60, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(96, cpu.registers.acc);
  assert_eq!(96, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_2() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x50;
  const ROP: u8 = 0x50;

  // decimal values sanity check
  assert_eq!(80, LOP);
  assert_eq!(80, ROP);
  assert_eq!(80, LOP as i8);
  assert_eq!(80, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0xa0, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(160, cpu.registers.acc);
  assert_eq!(160, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_3() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x50;
  const ROP: u8 = 0x90;

  // decimal values sanity check
  assert_eq!(80, LOP);
  assert_eq!(144, ROP);
  assert_eq!(80, LOP as i8);
  assert_eq!(-112, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0xe0, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(224, cpu.registers.acc);
  assert_eq!(-32, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_4() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x50;
  const ROP: u8 = 0xd0;

  // decimal values sanity check
  assert_eq!(80, LOP);
  assert_eq!(208, ROP);
  assert_eq!(80, LOP as i8);
  assert_eq!(-48, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0x20, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(32, cpu.registers.acc);
  assert_eq!(32, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_5() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0xd0;
  const ROP: u8 = 0x10;

  // decimal values sanity check
  assert_eq!(208, LOP);
  assert_eq!(16, ROP);
  assert_eq!(-48, LOP as i8);
  assert_eq!(16, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0xe0, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(224, cpu.registers.acc);
  assert_eq!(-32, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_6() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0xd0;
  const ROP: u8 = 0x50;

  // decimal values sanity check
  assert_eq!(208, LOP);
  assert_eq!(80, ROP);
  assert_eq!(-48, LOP as i8);
  assert_eq!(80, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0x20, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(32, cpu.registers.acc);
  assert_eq!(32, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_7() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0xd0;
  const ROP: u8 = 0x90;

  // decimal values sanity check
  assert_eq!(208, LOP);
  assert_eq!(144, ROP);
  assert_eq!(-48, LOP as i8);
  assert_eq!(-112, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0x60, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(96, cpu.registers.acc);
  assert_eq!(96, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_8() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0xd0;
  const ROP: u8 = 0xd0;

  // decimal values sanity check
  assert_eq!(208, LOP);
  assert_eq!(208, ROP);
  assert_eq!(-48, LOP as i8);
  assert_eq!(-48, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0xa0, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(160, cpu.registers.acc);
  assert_eq!(-96, cpu.registers.acc as i8);
}
<commit_msg>Fix another warning<commit_after>extern crate rs_nes;

use rs_nes::cpu::*;

/// ## Sign and zero flag tests
///
/// These tests check the presence of the sign and zero flag.

#[test]
fn adc_flags_sign_and_zero_1() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x0;
  const ROP: u8 = 0x0;

  // decimal values sanity check
  assert_eq!(0, LOP);
  assert_eq!(0, ROP);
  assert_eq!(0, LOP as i8);
  assert_eq!(0, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_ZERO));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_SIGN));
  assert_eq!(0x0, cpu.registers.acc);

  // decimal values sannity check
  assert_eq!(0, cpu.registers.acc);
  assert_eq!(0, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_sign_and_zero_2() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x0;
  const ROP: u8 = 0x1;

  // decimal values sanity check
  assert_eq!(0, LOP);
  assert_eq!(1, ROP);
  assert_eq!(0, LOP as i8);
  assert_eq!(1, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_ZERO));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_SIGN));
  assert_eq!(0x1, cpu.registers.acc);

  // decimal values sannity check
  assert_eq!(1, cpu.registers.acc);
  assert_eq!(1, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_sign_and_zero_3() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x0;
  const ROP: u8 = 0xff;;

  // decimal values sanity check
  assert_eq!(0, LOP);
  assert_eq!(255, ROP);
  assert_eq!(0, LOP as i8);
  assert_eq!(-1, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_ZERO));
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_SIGN));
  assert_eq!(0xff, cpu.registers.acc);

  // decimal values sannity check
  assert_eq!(255, cpu.registers.acc);
  assert_eq!(-1, cpu.registers.acc as i8);
}

/// ## Carry and overflow flag tests
///
/// The following tests check all permutations of the
/// 6th and 7th bits of both operands, asserting that
/// the overflow and carry bit is set appROPriately.
///
/// A truth table for these tests can be found here:
/// http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html

#[test]
fn adc_flags_carry_and_overflow_1() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x50;
  const ROP: u8 = 0x10;

  // decimal values sanity check
  assert_eq!(80, LOP);
  assert_eq!(16, ROP);
  assert_eq!(80, LOP as i8);
  assert_eq!(16, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0x60, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(96, cpu.registers.acc);
  assert_eq!(96, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_2() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x50;
  const ROP: u8 = 0x50;

  // decimal values sanity check
  assert_eq!(80, LOP);
  assert_eq!(80, ROP);
  assert_eq!(80, LOP as i8);
  assert_eq!(80, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0xa0, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(160, cpu.registers.acc);
}

#[test]
fn adc_flags_carry_and_overflow_3() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x50;
  const ROP: u8 = 0x90;

  // decimal values sanity check
  assert_eq!(80, LOP);
  assert_eq!(144, ROP);
  assert_eq!(80, LOP as i8);
  assert_eq!(-112, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0xe0, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(224, cpu.registers.acc);
  assert_eq!(-32, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_4() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0x50;
  const ROP: u8 = 0xd0;

  // decimal values sanity check
  assert_eq!(80, LOP);
  assert_eq!(208, ROP);
  assert_eq!(80, LOP as i8);
  assert_eq!(-48, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0x20, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(32, cpu.registers.acc);
  assert_eq!(32, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_5() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0xd0;
  const ROP: u8 = 0x10;

  // decimal values sanity check
  assert_eq!(208, LOP);
  assert_eq!(16, ROP);
  assert_eq!(-48, LOP as i8);
  assert_eq!(16, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0xe0, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(224, cpu.registers.acc);
  assert_eq!(-32, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_6() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0xd0;
  const ROP: u8 = 0x50;

  // decimal values sanity check
  assert_eq!(208, LOP);
  assert_eq!(80, ROP);
  assert_eq!(-48, LOP as i8);
  assert_eq!(80, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0x20, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(32, cpu.registers.acc);
  assert_eq!(32, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_7() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0xd0;
  const ROP: u8 = 0x90;

  // decimal values sanity check
  assert_eq!(208, LOP);
  assert_eq!(144, ROP);
  assert_eq!(-48, LOP as i8);
  assert_eq!(-112, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0x60, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(96, cpu.registers.acc);
  assert_eq!(96, cpu.registers.acc as i8);
}

#[test]
fn adc_flags_carry_and_overflow_8() {
  let mut cpu = Cpu6502::new();

  // operands
  const LOP: u8 = 0xd0;
  const ROP: u8 = 0xd0;

  // decimal values sanity check
  assert_eq!(208, LOP);
  assert_eq!(208, ROP);
  assert_eq!(-48, LOP as i8);
  assert_eq!(-48, ROP as i8);

  cpu.lda(LOP);
  cpu.adc(ROP);
  assert_eq!(true, cpu.registers.get_flag(rs_nes::cpu::FL_CARRY));
  assert_eq!(false, cpu.registers.get_flag(rs_nes::cpu::FL_OVERFLOW));
  assert_eq!(0xa0, cpu.registers.acc);

  // decimal values sanity check
  assert_eq!(160, cpu.registers.acc);
  assert_eq!(-96, cpu.registers.acc as i8);
}
<|endoftext|>


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


<commit_before>use super::{check, check_no_mismatches, check_types};

#[test]
fn block_expr_type_mismatch() {
    // FIXME fix double type mismatch
    check(
        r"
fn test() {
    let a: i32 = { 1i64 };
              // ^^^^^^^^ expected i32, got i64
                // ^^^^ expected i32, got i64
}
        ",
    );
}

#[test]
fn coerce_places() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
struct S<T> { a: T }

fn f<T>(_: &[T]) -> T { loop {} }
fn g<T>(_: S<&[T]>) -> T { loop {} }

fn gen<T>() -> *mut [T; 2] { loop {} }
fn test1<U>() -> *mut [U] {
    gen()
}

fn test2() {
    let arr: &[u8; 1] = &[1];

    let a: &[_] = arr;
    let b = f(arr);
    let c: &[_] = { arr };
    let d = g(S { a: arr });
    let e: [&[_]; 1] = [arr];
    let f: [&[_]; 2] = [arr; 2];
    let g: (&[_], &[_]) = (arr, arr);
}
"#,
    );
}

#[test]
fn let_stmt_coerce() {
    check_no_mismatches(
        r"
//- minicore: coerce_unsized
fn test() {
    let x: &[isize] = &[1];
    let x: *const [isize] = &[1];
}
",
    );
}

#[test]
fn custom_coerce_unsized() {
    check(
        r#"
//- minicore: coerce_unsized
use core::{marker::Unsize, ops::CoerceUnsized};

struct A<T: ?Sized>(*const T);
struct B<T: ?Sized>(*const T);
struct C<T: ?Sized> { inner: *const T }

impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {}

fn foo1<T>(x: A<[T]>) -> A<[T]> { x }
fn foo2<T>(x: B<[T]>) -> B<[T]> { x }
fn foo3<T>(x: C<[T]>) -> C<[T]> { x }

fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
    let d = foo1(a);
              // ^ expected A<[{unknown}]>, got A<[u8; 2]>
    let e = foo2(b);
     // ^ type: B<[u8]>
    let f = foo3(c);
     // ^ type: C<[u8]>
}
"#,
    );
}

#[test]
fn if_coerce() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn foo<T>(x: &[T]) -> &[T] { x }
fn test() {
    let x = if true {
        foo(&[1])
         // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
    } else {
        &[1]
    };
}
"#,
    );
}

#[test]
fn if_else_coerce() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn foo<T>(x: &[T]) -> &[T] { x }
fn test() {
    let x = if true {
        &[1]
    } else {
        foo(&[1])
    };
}
"#,
    )
}

#[test]
fn match_first_coerce() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn foo<T>(x: &[T]) -> &[T] { x }
fn test(i: i32) {
    let x = match i {
        2 => foo(&[2]),
              // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
        1 => &[1],
        _ => &[3],
    };
}
"#,
    );
}

#[test]
fn match_second_coerce() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn foo<T>(x: &[T]) -> &[T] { loop {} }
                          // ^^^^^^^ adjustments: NeverToAny
fn test(i: i32) {
    let x = match i {
        1 => &[1],
        2 => foo(&[2]),
        _ => &[3],
    };
}
"#,
    );
}

#[test]
fn coerce_merge_one_by_one1() {
    cov_mark::check!(coerce_merge_fail_fallback);

    check(
        r"
fn test() {
    let t = &mut 1;
    let x = match 1 {
        1 => t as *mut i32,
        2 => t as &i32,
           //^^^^^^^^^ expected *mut i32, got &i32
        _ => t as *const i32,
    };
    x;
  //^ type: *const i32
}
        ",
    );
}

#[test]
fn return_coerce_unknown() {
    check_types(
        r"
fn foo() -> u32 {
    return unknown;
         //^^^^^^^ u32
}
        ",
    );
}

#[test]
fn coerce_autoderef() {
    check_no_mismatches(
        r"
struct Foo;
fn takes_ref_foo(x: &Foo) {}
fn test() {
    takes_ref_foo(&Foo);
    takes_ref_foo(&&Foo);
    takes_ref_foo(&&&Foo);
}",
    );
}

#[test]
fn coerce_autoderef_generic() {
    check_no_mismatches(
        r#"
struct Foo;
fn takes_ref<T>(x: &T) -> T { *x }
fn test() {
    takes_ref(&Foo);
    takes_ref(&&Foo);
    takes_ref(&&&Foo);
}
"#,
    );
}

#[test]
fn coerce_autoderef_block() {
    check_no_mismatches(
        r#"
//- minicore: deref
struct String {}
impl core::ops::Deref for String { type Target = str; }
fn takes_ref_str(x: &str) {}
fn returns_string() -> String { loop {} }
fn test() {
    takes_ref_str(&{ returns_string() });
}
"#,
    );
}

#[test]
fn closure_return_coerce() {
    check_no_mismatches(
        r"
fn foo() {
    let x = || {
        if true {
            return &1u32;
        }
        &&1u32
    };
}",
    );
}

#[test]
fn coerce_fn_item_to_fn_ptr() {
    check_no_mismatches(
        r"
fn foo(x: u32) -> isize { 1 }
fn test() {
    let f: fn(u32) -> isize = foo;
}",
    );
}

#[test]
fn coerce_fn_items_in_match_arms() {
    cov_mark::check!(coerce_fn_reification);

    check_types(
        r"
fn foo1(x: u32) -> isize { 1 }
fn foo2(x: u32) -> isize { 2 }
fn foo3(x: u32) -> isize { 3 }
fn test() {
    let x = match 1 {
        1 => foo1,
        2 => foo2,
        _ => foo3,
    };
    x;
  //^ fn(u32) -> isize
}",
    );
}

#[test]
fn coerce_closure_to_fn_ptr() {
    check_no_mismatches(
        r"
fn test() {
    let f: fn(u32) -> isize = |x| { 1 };
}",
    );
}

#[test]
fn coerce_placeholder_ref() {
    // placeholders should unify, even behind references
    check_no_mismatches(
        r"
struct S<T> { t: T }
impl<TT> S<TT> {
    fn get(&self) -> &TT {
        &self.t
    }
}",
    );
}

#[test]
fn coerce_unsize_array() {
    check_types(
        r#"
//- minicore: coerce_unsized
fn test() {
    let f: &[usize] = &[1, 2, 3];
                      //^ usize
}"#,
    );
}

#[test]
fn coerce_unsize_trait_object_simple() {
    check_types(
        r#"
//- minicore: coerce_unsized
trait Foo<T, U> {}
trait Bar<U, T, X>: Foo<T, U> {}
trait Baz<T, X>: Bar<usize, T, X> {}

struct S<T, X>;
impl<T, X> Foo<T, usize> for S<T, X> {}
impl<T, X> Bar<usize, T, X> for S<T, X> {}
impl<T, X> Baz<T, X> for S<T, X> {}

fn test() {
    let obj: &dyn Baz<i8, i16> = &S;
                                //^ S<i8, i16>
    let obj: &dyn Bar<_, i8, i16> = &S;
                                   //^ S<i8, i16>
    let obj: &dyn Foo<i8, _> = &S;
                              //^ S<i8, {unknown}>
}"#,
    );
}

#[test]
fn coerce_unsize_super_trait_cycle() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
trait A {}
trait B: C + A {}
trait C: B {}
trait D: C

struct S;
impl A for S {}
impl B for S {}
impl C for S {}
impl D for S {}

fn test() {
    let obj: &dyn D = &S;
    let obj: &dyn A = &S;
}
"#,
    );
}

#[test]
fn coerce_unsize_generic() {
    // FIXME: fix the type mismatches here
    check(
        r#"
//- minicore: coerce_unsized
struct Foo<T> { t: T };
struct Bar<T>(Foo<T>);

fn test() {
    let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] };
                                   //^^^^^^^^^ expected [usize], got [usize; 3]
    let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
                         //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &Bar<[usize]>, got &Bar<[i32; 3]>
}
"#,
    );
}

#[test]
fn coerce_unsize_apit() {
    // FIXME: #8984
    check(
        r#"
//- minicore: coerce_unsized
trait Foo {}

fn test(f: impl Foo) {
    let _: &dyn Foo = &f;
                    //^^ expected &dyn Foo, got &impl Foo
}
        "#,
    );
}

#[test]
fn two_closures_lub() {
    check_types(
        r#"
fn foo(c: i32) {
    let add = |a: i32, b: i32| a + b;
    let sub = |a, b| a - b;
            //^^^^^^^^^^^^ |i32, i32| -> i32
    if c > 42 { add } else { sub };
  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
}
        "#,
    )
}

#[test]
fn match_diverging_branch_1() {
    check_types(
        r#"
enum Result<T> { Ok(T), Err }
fn parse<T>() -> T { loop {} }

fn test() -> i32 {
    let a = match parse() {
        Ok(val) => val,
        Err => return 0,
    };
    a
  //^ i32
}
        "#,
    )
}

#[test]
fn match_diverging_branch_2() {
    // same as 1 except for order of branches
    check_types(
        r#"
enum Result<T> { Ok(T), Err }
fn parse<T>() -> T { loop {} }

fn test() -> i32 {
    let a = match parse() {
        Err => return 0,
        Ok(val) => val,
    };
    a
  //^ i32
}
        "#,
    )
}

#[test]
fn panic_macro() {
    check_no_mismatches(
        r#"
mod panic {
    #[macro_export]
    pub macro panic_2015 {
        () => (
            $crate::panicking::panic()
        ),
    }
}

mod panicking {
    pub fn panic() -> ! { loop {} }
}

#[rustc_builtin_macro = "core_panic"]
macro_rules! panic {
    // Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021`
    // depending on the edition of the caller.
    ($($arg:tt)*) => {
        /* compiler built-in */
    };
}

fn main() {
    panic!()
}
        "#,
    );
}

#[test]
fn coerce_unsize_expected_type() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn main() {
    let foo: &[u32] = &[1, 2];
    let foo: &[u32] = match true {
        true => &[1, 2],
        false => &[1, 2, 3],
    };
    let foo: &[u32] = if true {
        &[1, 2]
    } else {
        &[1, 2, 3]
    };
}
        "#,
    );
}

#[test]
fn coerce_array_elems_lub() {
    check_no_mismatches(
        r#"
fn f() {}
fn g() {}

fn test() {
    [f, g];
}
        "#,
    );
}
<commit_msg>Add some more adjustment test annotations<commit_after>use super::{check, check_no_mismatches, check_types};

#[test]
fn block_expr_type_mismatch() {
    // FIXME fix double type mismatch
    check(
        r"
fn test() {
    let a: i32 = { 1i64 };
              // ^^^^^^^^ expected i32, got i64
                // ^^^^ expected i32, got i64
}
        ",
    );
}

#[test]
fn coerce_places() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
struct S<T> { a: T }

fn f<T>(_: &[T]) -> T { loop {} }
fn g<T>(_: S<&[T]>) -> T { loop {} }

fn gen<T>() -> *mut [T; 2] { loop {} }
fn test1<U>() -> *mut [U] {
    gen()
}

fn test2() {
    let arr: &[u8; 1] = &[1];

    let a: &[_] = arr;
    let b = f(arr);
    let c: &[_] = { arr };
    let d = g(S { a: arr });
    let e: [&[_]; 1] = [arr];
    let f: [&[_]; 2] = [arr; 2];
    let g: (&[_], &[_]) = (arr, arr);
}
"#,
    );
}

#[test]
fn let_stmt_coerce() {
    check_no_mismatches(
        r"
//- minicore: coerce_unsized
fn test() {
    let x: &[isize] = &[1];
                   // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
    let x: *const [isize] = &[1];
                         // ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize)
}
",
    );
}

#[test]
fn custom_coerce_unsized() {
    check(
        r#"
//- minicore: coerce_unsized
use core::{marker::Unsize, ops::CoerceUnsized};

struct A<T: ?Sized>(*const T);
struct B<T: ?Sized>(*const T);
struct C<T: ?Sized> { inner: *const T }

impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {}

fn foo1<T>(x: A<[T]>) -> A<[T]> { x }
fn foo2<T>(x: B<[T]>) -> B<[T]> { x }
fn foo3<T>(x: C<[T]>) -> C<[T]> { x }

fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
    let d = foo1(a);
              // ^ expected A<[{unknown}]>, got A<[u8; 2]>
    let e = foo2(b);
     // ^ type: B<[u8]>
    let f = foo3(c);
     // ^ type: C<[u8]>
}
"#,
    );
}

#[test]
fn if_coerce() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn foo<T>(x: &[T]) -> &[T] { x }
fn test() {
    let x = if true {
        foo(&[1])
         // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
    } else {
        &[1]
    };
}
"#,
    );
}

#[test]
fn if_else_coerce() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn foo<T>(x: &[T]) -> &[T] { x }
fn test() {
    let x = if true {
        &[1]
    } else {
        foo(&[1])
    };
}
"#,
    )
}

#[test]
fn match_first_coerce() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn foo<T>(x: &[T]) -> &[T] { x }
fn test(i: i32) {
    let x = match i {
        2 => foo(&[2]),
              // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
        1 => &[1],
        _ => &[3],
    };
}
"#,
    );
}

#[test]
fn match_second_coerce() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn foo<T>(x: &[T]) -> &[T] { loop {} }
                          // ^^^^^^^ adjustments: NeverToAny
fn test(i: i32) {
    let x = match i {
        1 => &[1],
        2 => foo(&[2]),
        _ => &[3],
    };
}
"#,
    );
}

#[test]
fn coerce_merge_one_by_one1() {
    cov_mark::check!(coerce_merge_fail_fallback);

    check(
        r"
fn test() {
    let t = &mut 1;
    let x = match 1 {
        1 => t as *mut i32,
        2 => t as &i32,
           //^^^^^^^^^ expected *mut i32, got &i32
        _ => t as *const i32,
          // ^^^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer)

    };
    x;
  //^ type: *const i32

}
        ",
    );
}

#[test]
fn return_coerce_unknown() {
    check_types(
        r"
fn foo() -> u32 {
    return unknown;
         //^^^^^^^ u32
}
        ",
    );
}

#[test]
fn coerce_autoderef() {
    check_no_mismatches(
        r"
struct Foo;
fn takes_ref_foo(x: &Foo) {}
fn test() {
    takes_ref_foo(&Foo);
    takes_ref_foo(&&Foo);
    takes_ref_foo(&&&Foo);
}",
    );
}

#[test]
fn coerce_autoderef_generic() {
    check_no_mismatches(
        r#"
struct Foo;
fn takes_ref<T>(x: &T) -> T { *x }
fn test() {
    takes_ref(&Foo);
    takes_ref(&&Foo);
    takes_ref(&&&Foo);
}
"#,
    );
}

#[test]
fn coerce_autoderef_block() {
    check_no_mismatches(
        r#"
//- minicore: deref
struct String {}
impl core::ops::Deref for String { type Target = str; }
fn takes_ref_str(x: &str) {}
fn returns_string() -> String { loop {} }
fn test() {
    takes_ref_str(&{ returns_string() });
}
"#,
    );
}

#[test]
fn closure_return_coerce() {
    check_no_mismatches(
        r"
fn foo() {
    let x = || {
        if true {
            return &1u32;
        }
        &&1u32
    };
}",
    );
}

#[test]
fn coerce_fn_item_to_fn_ptr() {
    check_no_mismatches(
        r"
fn foo(x: u32) -> isize { 1 }
fn test() {
    let f: fn(u32) -> isize = foo;
                           // ^^^ adjustments: Pointer(ReifyFnPointer)
    let f: unsafe fn(u32) -> isize = foo;
                                  // ^^^ adjustments: Pointer(ReifyFnPointer)
}",
    );
}

#[test]
fn coerce_fn_items_in_match_arms() {
    cov_mark::check!(coerce_fn_reification);

    check_types(
        r"
fn foo1(x: u32) -> isize { 1 }
fn foo2(x: u32) -> isize { 2 }
fn foo3(x: u32) -> isize { 3 }
fn test() {
    let x = match 1 {
        1 => foo1,
        2 => foo2,
        _ => foo3,
    };
    x;
  //^ fn(u32) -> isize
}",
    );
}

#[test]
fn coerce_closure_to_fn_ptr() {
    check_no_mismatches(
        r"
fn test() {
    let f: fn(u32) -> isize = |x| { 1 };
}",
    );
}

#[test]
fn coerce_placeholder_ref() {
    // placeholders should unify, even behind references
    check_no_mismatches(
        r"
struct S<T> { t: T }
impl<TT> S<TT> {
    fn get(&self) -> &TT {
        &self.t
    }
}",
    );
}

#[test]
fn coerce_unsize_array() {
    check_types(
        r#"
//- minicore: coerce_unsized
fn test() {
    let f: &[usize] = &[1, 2, 3];
                      //^ usize
}"#,
    );
}

#[test]
fn coerce_unsize_trait_object_simple() {
    check_types(
        r#"
//- minicore: coerce_unsized
trait Foo<T, U> {}
trait Bar<U, T, X>: Foo<T, U> {}
trait Baz<T, X>: Bar<usize, T, X> {}

struct S<T, X>;
impl<T, X> Foo<T, usize> for S<T, X> {}
impl<T, X> Bar<usize, T, X> for S<T, X> {}
impl<T, X> Baz<T, X> for S<T, X> {}

fn test() {
    let obj: &dyn Baz<i8, i16> = &S;
                                //^ S<i8, i16>
    let obj: &dyn Bar<_, i8, i16> = &S;
                                   //^ S<i8, i16>
    let obj: &dyn Foo<i8, _> = &S;
                              //^ S<i8, {unknown}>
}"#,
    );
}

#[test]
fn coerce_unsize_super_trait_cycle() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
trait A {}
trait B: C + A {}
trait C: B {}
trait D: C

struct S;
impl A for S {}
impl B for S {}
impl C for S {}
impl D for S {}

fn test() {
    let obj: &dyn D = &S;
    let obj: &dyn A = &S;
}
"#,
    );
}

#[test]
fn coerce_unsize_generic() {
    // FIXME: fix the type mismatches here
    check(
        r#"
//- minicore: coerce_unsized
struct Foo<T> { t: T };
struct Bar<T>(Foo<T>);

fn test() {
    let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] };
                                   //^^^^^^^^^ expected [usize], got [usize; 3]
    let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
                         //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &Bar<[usize]>, got &Bar<[i32; 3]>
}
"#,
    );
}

#[test]
fn coerce_unsize_apit() {
    // FIXME: #8984
    check(
        r#"
//- minicore: coerce_unsized
trait Foo {}

fn test(f: impl Foo) {
    let _: &dyn Foo = &f;
                    //^^ expected &dyn Foo, got &impl Foo
}
        "#,
    );
}

#[test]
fn two_closures_lub() {
    check_types(
        r#"
fn foo(c: i32) {
    let add = |a: i32, b: i32| a + b;
    let sub = |a, b| a - b;
            //^^^^^^^^^^^^ |i32, i32| -> i32
    if c > 42 { add } else { sub };
  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
}
        "#,
    )
}

#[test]
fn match_diverging_branch_1() {
    check_types(
        r#"
enum Result<T> { Ok(T), Err }
fn parse<T>() -> T { loop {} }

fn test() -> i32 {
    let a = match parse() {
        Ok(val) => val,
        Err => return 0,
    };
    a
  //^ i32
}
        "#,
    )
}

#[test]
fn match_diverging_branch_2() {
    // same as 1 except for order of branches
    check_types(
        r#"
enum Result<T> { Ok(T), Err }
fn parse<T>() -> T { loop {} }

fn test() -> i32 {
    let a = match parse() {
        Err => return 0,
        Ok(val) => val,
    };
    a
  //^ i32
}
        "#,
    )
}

#[test]
fn panic_macro() {
    check_no_mismatches(
        r#"
mod panic {
    #[macro_export]
    pub macro panic_2015 {
        () => (
            $crate::panicking::panic()
        ),
    }
}

mod panicking {
    pub fn panic() -> ! { loop {} }
}

#[rustc_builtin_macro = "core_panic"]
macro_rules! panic {
    // Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021`
    // depending on the edition of the caller.
    ($($arg:tt)*) => {
        /* compiler built-in */
    };
}

fn main() {
    panic!()
}
        "#,
    );
}

#[test]
fn coerce_unsize_expected_type() {
    check_no_mismatches(
        r#"
//- minicore: coerce_unsized
fn main() {
    let foo: &[u32] = &[1, 2];
    let foo: &[u32] = match true {
        true => &[1, 2],
        false => &[1, 2, 3],
    };
    let foo: &[u32] = if true {
        &[1, 2]
    } else {
        &[1, 2, 3]
    };
}
        "#,
    );
}

#[test]
fn coerce_array_elems_lub() {
    check_no_mismatches(
        r#"
fn f() {}
fn g() {}

fn test() {
    [f, g];
}
        "#,
    );
}
<|endoftext|>


<commit_before>use bytes::{BytesMut, BufMut};
use futures::{Future, IntoFuture, Sink, Stream as FuturesStream, Poll};
use futures::future::Either;
use postgres_shared::params::Host;
use postgres_protocol::message::backend;
use postgres_protocol::message::frontend;
use std::io::{self, Read, Write};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::{Encoder, Decoder, Framed};
use tokio_core::net::TcpStream;
use tokio_core::reactor::Handle;
use tokio_dns;

#[cfg(unix)]
use tokio_uds::UnixStream;

use {TlsMode, Error, BoxedFuture};
use error;
use tls::TlsStream;

pub type PostgresStream = Framed<Box<TlsStream>, PostgresCodec>;

pub fn connect(
    host: Host,
    port: u16,
    tls_mode: TlsMode,
    handle: &Handle,
) -> Box<Future<Item = PostgresStream, Error = Error> + Send> {
    let inner = match host {
        Host::Tcp(ref host) => {
            Either::A(
                tokio_dns::tcp_connect((&**host, port), handle.remote().clone())
                    .map(|s| Stream(InnerStream::Tcp(s)))
                    .map_err(error::io),
            )
        }
        #[cfg(unix)]
        Host::Unix(ref host) => {
            let addr = host.join(format!(".s.PGSQL.{}", port));
            Either::B(
                UnixStream::connect(addr, handle)
                    .map(|s| Stream(InnerStream::Unix(s)))
                    .map_err(error::io)
                    .into_future(),
            )
        }
        #[cfg(not(unix))]
        Host::Unix(_) => {
            Either::B(
                Err(ConnectError::ConnectParams(
                    "unix sockets are not supported on this \
                                                       platform"
                        .into(),
                )).into_future(),
            )
        }
    };

    let (required, handshaker) = match tls_mode {
        TlsMode::Require(h) => (true, h),
        TlsMode::Prefer(h) => (false, h),
        TlsMode::None => {
            return inner
                .map(|s| {
                    let s: Box<TlsStream> = Box::new(s);
                    s.framed(PostgresCodec)
                })
                .boxed2()
        }
    };

    inner
        .map(|s| s.framed(SslCodec))
        .and_then(|s| {
            let mut buf = vec![];
            frontend::ssl_request(&mut buf);
            s.send(buf).map_err(error::io)
        })
        .and_then(|s| s.into_future().map_err(|e| error::io(e.0)))
        .and_then(move |(m, s)| {
            let s = s.into_inner();
            match (m, required) {
                (Some(b'N'), true) => {
                    Either::A(
                        Err(error::tls("the server does not support TLS".into())).into_future(),
                    )
                }
                (Some(b'N'), false) => {
                    let s: Box<TlsStream> = Box::new(s);
                    Either::A(Ok(s).into_future())
                }
                (None, _) => {
                    Either::A(
                        Err(error::io(io::Error::new(
                            io::ErrorKind::UnexpectedEof,
                            "unexpected EOF",
                        ))).into_future(),
                    )
                }
                _ => {
                    let host = match host {
                        Host::Tcp(ref host) => host,
                        Host::Unix(_) => unreachable!(),
                    };
                    Either::B(handshaker.handshake(host, s).map_err(error::tls))
                }
            }
        })
        .map(|s| s.framed(PostgresCodec))
        .boxed2()
}

/// A raw connection to the database.
pub struct Stream(InnerStream);

enum InnerStream {
    Tcp(TcpStream),
    #[cfg(unix)]
    Unix(UnixStream),
}

impl Read for Stream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.read(buf),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.read(buf),
        }
    }
}

impl Write for Stream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.write(buf),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.flush(),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.flush(),
        }
    }
}

impl AsyncRead for Stream {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        match self.0 {
            InnerStream::Tcp(ref s) => s.prepare_uninitialized_buffer(buf),
            #[cfg(unix)]
            InnerStream::Unix(ref s) => s.prepare_uninitialized_buffer(buf),
        }
    }

    fn read_buf<B>(&mut self, buf: &mut B) -> Poll<usize, io::Error>
    where
        B: BufMut,
    {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.read_buf(buf),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.read_buf(buf),
        }
    }
}

impl AsyncWrite for Stream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.shutdown(),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.shutdown(),
        }
    }
}

pub struct PostgresCodec;

impl Decoder for PostgresCodec {
    type Item = backend::Message;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<backend::Message>> {
        backend::Message::parse(buf)
    }
}

impl Encoder for PostgresCodec {
    type Item = Vec<u8>;
    type Error = io::Error;

    fn encode(&mut self, msg: Vec<u8>, buf: &mut BytesMut) -> io::Result<()> {
        buf.extend(&msg);
        Ok(())
    }
}

struct SslCodec;

impl Decoder for SslCodec {
    type Item = u8;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<u8>> {
        if buf.is_empty() {
            Ok(None)
        } else {
            Ok(Some(buf.split_to(1)[0]))
        }
    }
}

impl Encoder for SslCodec {
    type Item = Vec<u8>;
    type Error = io::Error;

    fn encode(&mut self, msg: Vec<u8>, buf: &mut BytesMut) -> io::Result<()> {
        buf.extend(&msg);
        Ok(())
    }
}
<commit_msg>Correct build error on non-unix targets for tokio-postgres<commit_after>use bytes::{BytesMut, BufMut};
use futures::{Future, IntoFuture, Sink, Stream as FuturesStream, Poll};
use futures::future::Either;
use postgres_shared::params::Host;
use postgres_protocol::message::backend;
use postgres_protocol::message::frontend;
use std::io::{self, Read, Write};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::{Encoder, Decoder, Framed};
use tokio_core::net::TcpStream;
use tokio_core::reactor::Handle;
use tokio_dns;

#[cfg(unix)]
use tokio_uds::UnixStream;

use {TlsMode, Error, BoxedFuture};
use error;
use tls::TlsStream;

pub type PostgresStream = Framed<Box<TlsStream>, PostgresCodec>;

pub fn connect(
    host: Host,
    port: u16,
    tls_mode: TlsMode,
    handle: &Handle,
) -> Box<Future<Item = PostgresStream, Error = Error> + Send> {
    let inner = match host {
        Host::Tcp(ref host) => {
            Either::A(
                tokio_dns::tcp_connect((&**host, port), handle.remote().clone())
                    .map(|s| Stream(InnerStream::Tcp(s)))
                    .map_err(error::io),
            )
        }
        #[cfg(unix)]
        Host::Unix(ref host) => {
            let addr = host.join(format!(".s.PGSQL.{}", port));
            Either::B(
                UnixStream::connect(addr, handle)
                    .map(|s| Stream(InnerStream::Unix(s)))
                    .map_err(error::io)
                    .into_future(),
            )
        }
        #[cfg(not(unix))]
        Host::Unix(_) => {
            Either::B(
                Err(error::connect(
                    "unix sockets are not supported on this \
                                                       platform"
                        .into(),
                )).into_future(),
            )
        }
    };

    let (required, handshaker) = match tls_mode {
        TlsMode::Require(h) => (true, h),
        TlsMode::Prefer(h) => (false, h),
        TlsMode::None => {
            return inner
                .map(|s| {
                    let s: Box<TlsStream> = Box::new(s);
                    s.framed(PostgresCodec)
                })
                .boxed2()
        }
    };

    inner
        .map(|s| s.framed(SslCodec))
        .and_then(|s| {
            let mut buf = vec![];
            frontend::ssl_request(&mut buf);
            s.send(buf).map_err(error::io)
        })
        .and_then(|s| s.into_future().map_err(|e| error::io(e.0)))
        .and_then(move |(m, s)| {
            let s = s.into_inner();
            match (m, required) {
                (Some(b'N'), true) => {
                    Either::A(
                        Err(error::tls("the server does not support TLS".into())).into_future(),
                    )
                }
                (Some(b'N'), false) => {
                    let s: Box<TlsStream> = Box::new(s);
                    Either::A(Ok(s).into_future())
                }
                (None, _) => {
                    Either::A(
                        Err(error::io(io::Error::new(
                            io::ErrorKind::UnexpectedEof,
                            "unexpected EOF",
                        ))).into_future(),
                    )
                }
                _ => {
                    let host = match host {
                        Host::Tcp(ref host) => host,
                        Host::Unix(_) => unreachable!(),
                    };
                    Either::B(handshaker.handshake(host, s).map_err(error::tls))
                }
            }
        })
        .map(|s| s.framed(PostgresCodec))
        .boxed2()
}

/// A raw connection to the database.
pub struct Stream(InnerStream);

enum InnerStream {
    Tcp(TcpStream),
    #[cfg(unix)]
    Unix(UnixStream),
}

impl Read for Stream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.read(buf),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.read(buf),
        }
    }
}

impl Write for Stream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.write(buf),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.flush(),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.flush(),
        }
    }
}

impl AsyncRead for Stream {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        match self.0 {
            InnerStream::Tcp(ref s) => s.prepare_uninitialized_buffer(buf),
            #[cfg(unix)]
            InnerStream::Unix(ref s) => s.prepare_uninitialized_buffer(buf),
        }
    }

    fn read_buf<B>(&mut self, buf: &mut B) -> Poll<usize, io::Error>
    where
        B: BufMut,
    {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.read_buf(buf),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.read_buf(buf),
        }
    }
}

impl AsyncWrite for Stream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        match self.0 {
            InnerStream::Tcp(ref mut s) => s.shutdown(),
            #[cfg(unix)]
            InnerStream::Unix(ref mut s) => s.shutdown(),
        }
    }
}

pub struct PostgresCodec;

impl Decoder for PostgresCodec {
    type Item = backend::Message;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<backend::Message>> {
        backend::Message::parse(buf)
    }
}

impl Encoder for PostgresCodec {
    type Item = Vec<u8>;
    type Error = io::Error;

    fn encode(&mut self, msg: Vec<u8>, buf: &mut BytesMut) -> io::Result<()> {
        buf.extend(&msg);
        Ok(())
    }
}

struct SslCodec;

impl Decoder for SslCodec {
    type Item = u8;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<u8>> {
        if buf.is_empty() {
            Ok(None)
        } else {
            Ok(Some(buf.split_to(1)[0]))
        }
    }
}

impl Encoder for SslCodec {
    type Item = Vec<u8>;
    type Error = io::Error;

    fn encode(&mut self, msg: Vec<u8>, buf: &mut BytesMut) -> io::Result<()> {
        buf.extend(&msg);
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Seek;
use std::io::Write;
use std::path::Path;

extern crate env_logger;
extern crate tools;
use crate::languages::FormatAs;
use tools::config;
use tools::describe;
use tools::file_format::analysis::{read_analysis, read_jumps, read_source};
use tools::find_source_file;
use tools::format::format_file_data;
use tools::git_ops;
use tools::languages;

use tools::output::{PanelItem, PanelSection};

extern crate rustc_serialize;
use rustc_serialize::json;

fn read_json_from_file(path: &str) -> Option<json::Object> {
    let components_file = File::open(path).ok()?;
    let mut reader = BufReader::new(&components_file);
    json::Json::from_reader(&mut reader).ok()?.into_object()
}

/// For a given path, looks up the bugzilla product and component and
/// returns it in a tuple if it could be found. The JSON data format
/// comes from https://searchfox.org/mozilla-central/rev/47edbd91c43db6229cf32d1fc4bae9b325b9e2d0/python/mozbuild/mozbuild/frontend/mach_commands.py#209-223,243
/// and is fairly straightforward.
fn get_bugzilla_component<'a>(
    bugzilla_data: &'a json::Object,
    path: &str,
) -> Option<(&'a str, &'a str)> {
    let mut path_obj = bugzilla_data.get("paths")?;
    for path_component in path.split('/') {
        path_obj = path_obj.as_object()?.get(path_component)?;
    }
    let component_id = path_obj.as_i64()?.to_string();
    let mut result_iter = bugzilla_data
        .get("components")?
        .as_object()?
        .get(&component_id)?
        .as_array()?
        .iter();
    let product = result_iter.next()?.as_string()?;
    let component = result_iter.next()?.as_string()?;
    Some((product, component))
}

fn main() {
    env_logger::init();

    let args: Vec<_> = env::args().collect();
    let (base_args, fname_args) = args.split_at(3);

    let cfg = config::load(&base_args[1], true);
    println!("Config file read");

    let tree_name = &base_args[2];
    let tree_config = cfg.trees.get(tree_name).unwrap();

    let jumps_fname = format!("{}/jumps", tree_config.paths.index_path);
    //let jumps : std::collections::HashMap<String, tools::analysis::Jump> = std::collections::HashMap::new();
    let jumps = read_jumps(&jumps_fname);
    println!("Jumps read");

    let bugzilla_fname = format!("{}/bugzilla-components.json", tree_config.paths.index_path);
    let bugzilla_data = read_json_from_file(&bugzilla_fname);
    if bugzilla_data.is_some() {
        println!("Bugzilla components read");
    } else {
        println!("No bugzilla-components.json file found");
    }

    let (blame_commit, head_oid) = match &tree_config.git {
        &Some(ref git) => {
            let head_oid = git.repo.refname_to_id("HEAD").unwrap();
            let blame_commit = if let Some(ref blame_repo) = git.blame_repo {
                let blame_oid = blame_repo.refname_to_id("HEAD").unwrap();
                Some(blame_repo.find_commit(blame_oid).unwrap())
            } else {
                None
            };
            (blame_commit, Some(head_oid))
        }
        &None => (None, None),
    };

    let head_commit =
        head_oid.and_then(|oid| tree_config.git.as_ref().unwrap().repo.find_commit(oid).ok());

    let mut extension_mapping = HashMap::new();
    extension_mapping.insert("cpp", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("cc", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("cxx", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("h", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hh", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hpp", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hxx", ("source", vec!["cpp", "cc", "cxx"]));

    let mut diff_cache = git_ops::TreeDiffCache::new();
    for path in fname_args {
        println!("File {}", path);

        let output_fname = format!("{}/file/{}", tree_config.paths.index_path, path);
        let source_fname = find_source_file(
            path,
            &tree_config.paths.files_path,
            &tree_config.paths.objdir_path,
        );

        let format = languages::select_formatting(path);

        let output_file = File::create(output_fname).unwrap();
        let mut writer = BufWriter::new(output_file);

        let source_file = match File::open(source_fname.clone()) {
            Ok(f) => f,
            Err(_) => {
                println!("Unable to open file");
                continue;
            }
        };

        let path_wrapper = Path::new(&source_fname);
        let metadata = fs::symlink_metadata(path_wrapper).unwrap();
        if metadata.file_type().is_symlink() {
            let dest = fs::read_link(path_wrapper).unwrap();
            write!(writer, "Symlink to {}", dest.to_str().unwrap()).unwrap();
            continue;
        }

        let mut reader = BufReader::new(&source_file);

        match format {
            FormatAs::Binary => {
                let _ = io::copy(&mut reader, &mut writer);
                continue;
            }
            _ => {}
        };

        let analysis_fname = format!("{}/analysis/{}", tree_config.paths.index_path, path);
        let analysis = read_analysis(&analysis_fname, &mut read_source);

        let mut input = String::new();
        match reader.read_to_string(&mut input) {
            Ok(_) => {}
            Err(_) => {
                let mut bytes = Vec::new();
                reader.seek(std::io::SeekFrom::Start(0)).unwrap();
                match reader.read_to_end(&mut bytes) {
                    Ok(_) => {
                        input.push_str(&bytes.iter().map(|c| *c as char).collect::<String>());
                    }
                    Err(e) => {
                        println!("Unable to read file: {:?}", e);
                        continue;
                    }
                }
            }
        }

        if let Some(file_description) = describe::describe_file(&input, &path_wrapper, &format) {
            let description_fname =
                format!("{}/description/{}", tree_config.paths.index_path, path);
            let description_file = File::create(description_fname).unwrap();
            let mut desc_writer = BufWriter::new(description_file);
            write!(desc_writer, "{}", file_description).unwrap();
        }

        let extension = path_wrapper
            .extension()
            .unwrap_or(&OsStr::new(""))
            .to_str()
            .unwrap();
        let show_header = match extension_mapping.get(extension) {
            Some(&(ref description, ref try_extensions)) => {
                let mut result = None;
                for try_ext in try_extensions {
                    let try_buf = path_wrapper.with_extension(try_ext);
                    let try_path = try_buf.as_path();
                    if try_path.exists() {
                        let (path_base, _) = path.split_at(path.len() - extension.len() - 1);
                        result = Some((
                            description.to_owned(),
                            format!("/{}/source/{}.{}", tree_name, path_base, try_ext),
                        ));
                        break;
                    }
                }
                result
            }
            None => None,
        };

        let mut panel = vec![];

        let mut source_panel_items = vec![];
        if let Some((other_desc, other_path)) = show_header {
            source_panel_items.push(PanelItem {
                title: format!("Go to {} file", other_desc),
                link: other_path,
                update_link_lineno: "",
                accel_key: None,
            });
        };
        if let Some(ref bugzilla) = bugzilla_data {
            if !path.contains("__GENERATED__") {
                if let Some((product, component)) = get_bugzilla_component(bugzilla, &path) {
                    source_panel_items.push(PanelItem {
                        title: format!("File a bug in {} :: {}", product, component),
                        link: format!(
                            "https://bugzilla.mozilla.org/enter_bug.cgi?product={}&component={}",
                            product.replace("&", "%26"),
                            component.replace("&", "%26")
                        ),
                        update_link_lineno: "",
                        accel_key: None,
                    });
                }
            }
        }

        if !source_panel_items.is_empty() {
            panel.push(PanelSection {
                name: "Source code".to_owned(),
                items: source_panel_items,
            });
        };

        if let Some(oid) = head_oid {
            if !path.contains("__GENERATED__") {
                let mut vcs_panel_items = vec![];
                vcs_panel_items.push(PanelItem {
                    title: "Permalink".to_owned(),
                    link: format!("/{}/rev/{}/{}", tree_name, oid, path),
                    update_link_lineno: "#{}",
                    accel_key: Some('Y'),
                });
                if let Some(ref hg_root) = tree_config.paths.hg_root {
                    vcs_panel_items.push(PanelItem {
                        title: "Log".to_owned(),
                        link: format!("{}/log/tip/{}", hg_root, path),
                        update_link_lineno: "",
                        accel_key: Some('L'),
                    });
                    vcs_panel_items.push(PanelItem {
                        title: "Raw".to_owned(),
                        link: format!("{}/raw-file/tip/{}", hg_root, path),
                        update_link_lineno: "",
                        accel_key: Some('R'),
                    });
                }
                if tree_config.paths.git_blame_path.is_some() {
                    vcs_panel_items.push(PanelItem {
                        title: "Blame".to_owned(),
                        link: "javascript:alert('Hover over the gray bar on the left to see blame information.')".to_owned(),
                        update_link_lineno: "",
                        accel_key: None,
                    });
                }
                panel.push(PanelSection {
                    name: "Revision control".to_owned(),
                    items: vcs_panel_items,
                });
            }
        }

        let mut tools_items = vec![];
        if let Some(ref hg_root) = tree_config.paths.hg_root {
            tools_items.push(PanelItem {
                title: "HG Web".to_owned(),
                link: format!("{}/file/tip/{}", hg_root, path),
                update_link_lineno: "#{}",
                accel_key: None,
            });
        }
        if let Some(ref ccov_root) = tree_config.paths.ccov_root {
            tools_items.push(PanelItem {
                title: "Code Coverage".to_owned(),
                link: format!("{}#revision=latest&path={}&view=file", ccov_root, path),
                update_link_lineno: "#{}",
                accel_key: None,
            });
        }
        if let Some(ref dxr_root) = tree_config.paths.dxr_root {
            tools_items.push(PanelItem {
                title: "DXR".to_owned(),
                link: format!("{}/source/{}", dxr_root, path),
                update_link_lineno: "#{}",
                accel_key: None,
            });
        }
        if let Some(ref github) = tree_config.paths.github_repo {
            match Path::new(path).extension().and_then(OsStr::to_str) {
                Some("md") | Some("rst") => {
                    tools_items.push(PanelItem {
                        title: "Rendered view".to_owned(),
                        link: format!(
                            "{}/blob/{}/{}",
                            github,
                            head_oid.map_or("master".to_string(), |x| x.to_string()),
                            path
                        ),
                        update_link_lineno: "",
                        accel_key: None,
                    });
                }
                _ => (),
            };
        }
        if !tools_items.is_empty() {
            panel.push(PanelSection {
                name: "Other Tools".to_owned(),
                items: tools_items,
            });
        }

        format_file_data(
            &cfg,
            tree_name,
            &panel,
            &head_commit,
            &blame_commit,
            path,
            input,
            &jumps,
            &analysis,
            &mut writer,
            Some(&mut diff_cache),
        )
        .unwrap();
    }
}
<commit_msg>Bug 1600599 - Fix code coverage links when lines are selected.<commit_after>use std::collections::HashMap;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Read;
use std::io::Seek;
use std::io::Write;
use std::path::Path;

extern crate env_logger;
extern crate tools;
use crate::languages::FormatAs;
use tools::config;
use tools::describe;
use tools::file_format::analysis::{read_analysis, read_jumps, read_source};
use tools::find_source_file;
use tools::format::format_file_data;
use tools::git_ops;
use tools::languages;

use tools::output::{PanelItem, PanelSection};

extern crate rustc_serialize;
use rustc_serialize::json;

fn read_json_from_file(path: &str) -> Option<json::Object> {
    let components_file = File::open(path).ok()?;
    let mut reader = BufReader::new(&components_file);
    json::Json::from_reader(&mut reader).ok()?.into_object()
}

/// For a given path, looks up the bugzilla product and component and
/// returns it in a tuple if it could be found. The JSON data format
/// comes from https://searchfox.org/mozilla-central/rev/47edbd91c43db6229cf32d1fc4bae9b325b9e2d0/python/mozbuild/mozbuild/frontend/mach_commands.py#209-223,243
/// and is fairly straightforward.
fn get_bugzilla_component<'a>(
    bugzilla_data: &'a json::Object,
    path: &str,
) -> Option<(&'a str, &'a str)> {
    let mut path_obj = bugzilla_data.get("paths")?;
    for path_component in path.split('/') {
        path_obj = path_obj.as_object()?.get(path_component)?;
    }
    let component_id = path_obj.as_i64()?.to_string();
    let mut result_iter = bugzilla_data
        .get("components")?
        .as_object()?
        .get(&component_id)?
        .as_array()?
        .iter();
    let product = result_iter.next()?.as_string()?;
    let component = result_iter.next()?.as_string()?;
    Some((product, component))
}

fn main() {
    env_logger::init();

    let args: Vec<_> = env::args().collect();
    let (base_args, fname_args) = args.split_at(3);

    let cfg = config::load(&base_args[1], true);
    println!("Config file read");

    let tree_name = &base_args[2];
    let tree_config = cfg.trees.get(tree_name).unwrap();

    let jumps_fname = format!("{}/jumps", tree_config.paths.index_path);
    //let jumps : std::collections::HashMap<String, tools::analysis::Jump> = std::collections::HashMap::new();
    let jumps = read_jumps(&jumps_fname);
    println!("Jumps read");

    let bugzilla_fname = format!("{}/bugzilla-components.json", tree_config.paths.index_path);
    let bugzilla_data = read_json_from_file(&bugzilla_fname);
    if bugzilla_data.is_some() {
        println!("Bugzilla components read");
    } else {
        println!("No bugzilla-components.json file found");
    }

    let (blame_commit, head_oid) = match &tree_config.git {
        &Some(ref git) => {
            let head_oid = git.repo.refname_to_id("HEAD").unwrap();
            let blame_commit = if let Some(ref blame_repo) = git.blame_repo {
                let blame_oid = blame_repo.refname_to_id("HEAD").unwrap();
                Some(blame_repo.find_commit(blame_oid).unwrap())
            } else {
                None
            };
            (blame_commit, Some(head_oid))
        }
        &None => (None, None),
    };

    let head_commit =
        head_oid.and_then(|oid| tree_config.git.as_ref().unwrap().repo.find_commit(oid).ok());

    let mut extension_mapping = HashMap::new();
    extension_mapping.insert("cpp", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("cc", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("cxx", ("header", vec!["h", "hh", "hpp", "hxx"]));
    extension_mapping.insert("h", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hh", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hpp", ("source", vec!["cpp", "cc", "cxx"]));
    extension_mapping.insert("hxx", ("source", vec!["cpp", "cc", "cxx"]));

    let mut diff_cache = git_ops::TreeDiffCache::new();
    for path in fname_args {
        println!("File {}", path);

        let output_fname = format!("{}/file/{}", tree_config.paths.index_path, path);
        let source_fname = find_source_file(
            path,
            &tree_config.paths.files_path,
            &tree_config.paths.objdir_path,
        );

        let format = languages::select_formatting(path);

        let output_file = File::create(output_fname).unwrap();
        let mut writer = BufWriter::new(output_file);

        let source_file = match File::open(source_fname.clone()) {
            Ok(f) => f,
            Err(_) => {
                println!("Unable to open file");
                continue;
            }
        };

        let path_wrapper = Path::new(&source_fname);
        let metadata = fs::symlink_metadata(path_wrapper).unwrap();
        if metadata.file_type().is_symlink() {
            let dest = fs::read_link(path_wrapper).unwrap();
            write!(writer, "Symlink to {}", dest.to_str().unwrap()).unwrap();
            continue;
        }

        let mut reader = BufReader::new(&source_file);

        match format {
            FormatAs::Binary => {
                let _ = io::copy(&mut reader, &mut writer);
                continue;
            }
            _ => {}
        };

        let analysis_fname = format!("{}/analysis/{}", tree_config.paths.index_path, path);
        let analysis = read_analysis(&analysis_fname, &mut read_source);

        let mut input = String::new();
        match reader.read_to_string(&mut input) {
            Ok(_) => {}
            Err(_) => {
                let mut bytes = Vec::new();
                reader.seek(std::io::SeekFrom::Start(0)).unwrap();
                match reader.read_to_end(&mut bytes) {
                    Ok(_) => {
                        input.push_str(&bytes.iter().map(|c| *c as char).collect::<String>());
                    }
                    Err(e) => {
                        println!("Unable to read file: {:?}", e);
                        continue;
                    }
                }
            }
        }

        if let Some(file_description) = describe::describe_file(&input, &path_wrapper, &format) {
            let description_fname =
                format!("{}/description/{}", tree_config.paths.index_path, path);
            let description_file = File::create(description_fname).unwrap();
            let mut desc_writer = BufWriter::new(description_file);
            write!(desc_writer, "{}", file_description).unwrap();
        }

        let extension = path_wrapper
            .extension()
            .unwrap_or(&OsStr::new(""))
            .to_str()
            .unwrap();
        let show_header = match extension_mapping.get(extension) {
            Some(&(ref description, ref try_extensions)) => {
                let mut result = None;
                for try_ext in try_extensions {
                    let try_buf = path_wrapper.with_extension(try_ext);
                    let try_path = try_buf.as_path();
                    if try_path.exists() {
                        let (path_base, _) = path.split_at(path.len() - extension.len() - 1);
                        result = Some((
                            description.to_owned(),
                            format!("/{}/source/{}.{}", tree_name, path_base, try_ext),
                        ));
                        break;
                    }
                }
                result
            }
            None => None,
        };

        let mut panel = vec![];

        let mut source_panel_items = vec![];
        if let Some((other_desc, other_path)) = show_header {
            source_panel_items.push(PanelItem {
                title: format!("Go to {} file", other_desc),
                link: other_path,
                update_link_lineno: "",
                accel_key: None,
            });
        };
        if let Some(ref bugzilla) = bugzilla_data {
            if !path.contains("__GENERATED__") {
                if let Some((product, component)) = get_bugzilla_component(bugzilla, &path) {
                    source_panel_items.push(PanelItem {
                        title: format!("File a bug in {} :: {}", product, component),
                        link: format!(
                            "https://bugzilla.mozilla.org/enter_bug.cgi?product={}&component={}",
                            product.replace("&", "%26"),
                            component.replace("&", "%26")
                        ),
                        update_link_lineno: "",
                        accel_key: None,
                    });
                }
            }
        }

        if !source_panel_items.is_empty() {
            panel.push(PanelSection {
                name: "Source code".to_owned(),
                items: source_panel_items,
            });
        };

        if let Some(oid) = head_oid {
            if !path.contains("__GENERATED__") {
                let mut vcs_panel_items = vec![];
                vcs_panel_items.push(PanelItem {
                    title: "Permalink".to_owned(),
                    link: format!("/{}/rev/{}/{}", tree_name, oid, path),
                    update_link_lineno: "#{}",
                    accel_key: Some('Y'),
                });
                if let Some(ref hg_root) = tree_config.paths.hg_root {
                    vcs_panel_items.push(PanelItem {
                        title: "Log".to_owned(),
                        link: format!("{}/log/tip/{}", hg_root, path),
                        update_link_lineno: "",
                        accel_key: Some('L'),
                    });
                    vcs_panel_items.push(PanelItem {
                        title: "Raw".to_owned(),
                        link: format!("{}/raw-file/tip/{}", hg_root, path),
                        update_link_lineno: "",
                        accel_key: Some('R'),
                    });
                }
                if tree_config.paths.git_blame_path.is_some() {
                    vcs_panel_items.push(PanelItem {
                        title: "Blame".to_owned(),
                        link: "javascript:alert('Hover over the gray bar on the left to see blame information.')".to_owned(),
                        update_link_lineno: "",
                        accel_key: None,
                    });
                }
                panel.push(PanelSection {
                    name: "Revision control".to_owned(),
                    items: vcs_panel_items,
                });
            }
        }

        let mut tools_items = vec![];
        if let Some(ref hg_root) = tree_config.paths.hg_root {
            tools_items.push(PanelItem {
                title: "HG Web".to_owned(),
                link: format!("{}/file/tip/{}", hg_root, path),
                update_link_lineno: "#{}",
                accel_key: None,
            });
        }
        if let Some(ref ccov_root) = tree_config.paths.ccov_root {
            tools_items.push(PanelItem {
                title: "Code Coverage".to_owned(),
                link: format!("{}#revision=latest&path={}&view=file", ccov_root, path),
                update_link_lineno: "&line={}",
                accel_key: None,
            });
        }
        if let Some(ref dxr_root) = tree_config.paths.dxr_root {
            tools_items.push(PanelItem {
                title: "DXR".to_owned(),
                link: format!("{}/source/{}", dxr_root, path),
                update_link_lineno: "#{}",
                accel_key: None,
            });
        }
        if let Some(ref github) = tree_config.paths.github_repo {
            match Path::new(path).extension().and_then(OsStr::to_str) {
                Some("md") | Some("rst") => {
                    tools_items.push(PanelItem {
                        title: "Rendered view".to_owned(),
                        link: format!(
                            "{}/blob/{}/{}",
                            github,
                            head_oid.map_or("master".to_string(), |x| x.to_string()),
                            path
                        ),
                        update_link_lineno: "",
                        accel_key: None,
                    });
                }
                _ => (),
            };
        }
        if !tools_items.is_empty() {
            panel.push(PanelSection {
                name: "Other Tools".to_owned(),
                items: tools_items,
            });
        }

        format_file_data(
            &cfg,
            tree_name,
            &panel,
            &head_commit,
            &blame_commit,
            path,
            input,
            &jumps,
            &analysis,
            &mut writer,
            Some(&mut diff_cache),
        )
        .unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate polydraw;

use polydraw::{Application, Renderer, Frame};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

struct Poly {
   points: Vec<Point>,
   color: RGB,
}

impl Poly {
   fn min_y_index(&self) -> usize {
      let mut max_after = false;

      let (first, rest) = self.points.split_first().unwrap();

      let mut min_y = first.y;
      let mut min_index: usize = 0;

      for (index, point) in rest.iter().enumerate() {
         if point.y > min_y {
            if max_after {
               return min_index;
            }
         } else {
            max_after = true;
            min_index = index + 1;
            min_y = point.y;
         }
      }

      min_index
   }

   fn max_y_index(&self) -> usize {
      let mut min_after = false;

      let (first, rest) = self.points.split_first().unwrap();

      let mut max_y = first.y;
      let mut max_index: usize = 0;

      for (index, point) in rest.iter().enumerate() {
         if point.y < max_y {
            if min_after {
               return max_index;
            }
         } else {
            min_after = true;
            max_index = index + 1;
            max_y = point.y;
         }
      }

      max_index
   }
}

struct Scene {
   polys: Vec<Poly>,
}

impl Scene {
   #[inline]
   fn new() -> Self {
      Scene {
         polys: Vec::new(),
      }
   }

   #[inline]
   pub fn push(&mut self, poly: Poly) {
      self.polys.push(poly);
   }
}

struct DevRenderer {
   scene: Scene,
}

impl DevRenderer {
   fn new(scene: Scene) -> Self {
      DevRenderer {
         scene: scene,
      }
   }

   fn _render_poly(&self, frame: &mut Frame, poly: &Poly) {
      let min_y_index = poly.min_y_index();
      let max_y_index = poly.max_y_index();

      println!("MIN [{:?}] = {:?}", min_y_index, poly.points[min_y_index]);

      println!("MAX [{:?}] = {:?}", max_y_index, poly.points[max_y_index]);

      for point in poly.points.iter() {
         frame.put_pixel(point.x as i32, point.y as i32, &poly.color)
      }
   }
}

impl Renderer for DevRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      for poly in self.scene.polys.iter() {
         self._render_poly(frame, poly);
      }

      panic!("Bye!");
   }
}

fn main() {
   let mut scene = Scene::new();

   let poly_a = Poly {
      points: vec![
         Point::new(1100, 400),
         Point::new(500, 700),
         Point::new(200, 200),
      ],
      color: RGB::new(34, 78, 29),
   };

   let poly_b = Poly {
      points: vec![
         Point::new(100, 500),
         Point::new(700, 100),
         Point::new(900, 200),
         Point::new(900, 1000),
      ],
      color: RGB::new(128, 59, 89),
   };

   scene.push(poly_a);
   scene.push(poly_b);

   let mut renderer = DevRenderer::new(scene);

   Application::new()
      .renderer(&mut renderer)
      .title("Dev Rasterizer")
      .size(800, 450)
      .run();
}

<commit_msg>Unified min max y calculation method<commit_after>extern crate polydraw;

use polydraw::{Application, Renderer, Frame};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

struct Poly {
   points: Vec<Point>,
   color: RGB,
}

impl Poly {
   fn min_max_y_index(&self) -> (usize, usize) {
      let (first, rest) = self.points.split_first().unwrap();

      let mut min_y = first.y;
      let mut max_y = min_y;
      let mut min_index: usize = 0;
      let mut max_index: usize = 0;

      for (index, point) in rest.iter().enumerate() {
         if point.y < min_y {
            min_index = index + 1;
            min_y = point.y;
         } else if point.y > max_y {
            max_index = index + 1;
            max_y = point.y;
         }
      }

      (min_index, max_index)
   }
}

struct Scene {
   polys: Vec<Poly>,
}

impl Scene {
   #[inline]
   fn new() -> Self {
      Scene {
         polys: Vec::new(),
      }
   }

   #[inline]
   pub fn push(&mut self, poly: Poly) {
      self.polys.push(poly);
   }
}

struct DevRenderer {
   scene: Scene,
}

impl DevRenderer {
   fn new(scene: Scene) -> Self {
      DevRenderer {
         scene: scene,
      }
   }

   fn _render_poly(&self, frame: &mut Frame, poly: &Poly) {
      let (min_y_index, max_y_index) = poly.min_max_y_index();

      println!("MIN [{:?}] = {:?}", min_y_index, poly.points[min_y_index]);

      println!("MAX [{:?}] = {:?}", max_y_index, poly.points[max_y_index]);

      for point in poly.points.iter() {
         frame.put_pixel(point.x as i32, point.y as i32, &poly.color)
      }
   }
}

impl Renderer for DevRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      for poly in self.scene.polys.iter() {
         self._render_poly(frame, poly);
      }

      panic!("Bye!");
   }
}

fn main() {
   let mut scene = Scene::new();

   let poly_a = Poly {
      points: vec![
         Point::new(1100, 400),
         Point::new(500, 700),
         Point::new(200, 200),
      ],
      color: RGB::new(34, 78, 29),
   };

   let poly_b = Poly {
      points: vec![
         Point::new(100, 500),
         Point::new(700, 100),
         Point::new(900, 200),
         Point::new(900, 1000),
      ],
      color: RGB::new(128, 59, 89),
   };

   scene.push(poly_a);
   scene.push(poly_b);

   let mut renderer = DevRenderer::new(scene);

   Application::new()
      .renderer(&mut renderer)
      .title("Dev Rasterizer")
      .size(800, 450)
      .run();
}

<|endoftext|>


<commit_before><error descr="Cannot declare a new module at this location">mod foo;</error>

#[path="helper.rs"]
mod foobar;

mod inner {
    <error descr="Unresolved module">mod bar;</error>
}

fn main() {
    mod foo {
        <error descr="Cannot declare a non-inline module inside a block unless it has a path attribute">mod bar;</error>
    }
}
<commit_msg>(T): add one more test case to Invalid Module Declarations test<commit_after><error descr="Cannot declare a new module at this location">mod foo;</error>

#[path="helper.rs"]
mod foobar;

<error descr="Unresolved module">#[path=""] mod nonono;</error>

mod inner {
    <error descr="Unresolved module">mod bar;</error>
}

fn main() {
    mod foo {
        <error descr="Cannot declare a non-inline module inside a block unless it has a path attribute">mod bar;</error>
    }
}
<|endoftext|>