<commit_before>//! Module for use with `#[serde(with = "...")]` to serialize a [`Uuid`]
//! as a `[u8; 16]`.
//!
//! [`Uuid`]: ../../struct.Uuid.html

/// Serializer for a [`Uuid`] into a `[u8; 16]`
///
/// [`Uuid`]: ../../struct.Uuid.html
pub fn serialize<S>(u: &crate::Uuid, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serde::Serialize::serialize(u.as_bytes(), serializer)
}

/// Deserializer from a `[u8; 16]` into a [`Uuid`]
///
/// [`Uuid`]: ../../struct.Uuid.html
pub fn deserialize<'de, D>(deserializer: D) -> Result<crate::Uuid, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let bytes: [u8; 16] = serde::Deserialize::deserialize(deserializer)?;

    Ok(crate::Uuid::from_bytes(bytes))
}

#[cfg(test)]
mod tests {

    use serde_test;

    #[test]
    fn test_serialize_compact() {
        #[derive(serde::Serialize, Debug, serde::Deserialize, PartialEq)]
        struct UuidContainer {
            #[serde(with = "super")]
            u: crate::Uuid,
        }
        use serde_test::Configure;

        let uuid_bytes = b"F9168C5E-CEB2-4F";
        let container = UuidContainer {
            u: crate::Uuid::from_slice(uuid_bytes).unwrap(),
        };

        // more complex because of the struct wrapping the actual UUID
        // serialization
        serde_test::assert_tokens(
            &container.compact(),
            &[
                serde_test::Token::Struct {
                    name: "UuidContainer",
                    len: 1,
                },
                serde_test::Token::Str("u"),
                serde_test::Token::Tuple { len: 16 },
                serde_test::Token::U8(uuid_bytes[0]),
                serde_test::Token::U8(uuid_bytes[1]),
                serde_test::Token::U8(uuid_bytes[2]),
                serde_test::Token::U8(uuid_bytes[3]),
                serde_test::Token::U8(uuid_bytes[4]),
                serde_test::Token::U8(uuid_bytes[5]),
                serde_test::Token::U8(uuid_bytes[6]),
                serde_test::Token::U8(uuid_bytes[7]),
                serde_test::Token::U8(uuid_bytes[8]),
                serde_test::Token::U8(uuid_bytes[9]),
                serde_test::Token::U8(uuid_bytes[10]),
                serde_test::Token::U8(uuid_bytes[11]),
                serde_test::Token::U8(uuid_bytes[12]),
                serde_test::Token::U8(uuid_bytes[13]),
                serde_test::Token::U8(uuid_bytes[14]),
                serde_test::Token::U8(uuid_bytes[15]),
                serde_test::Token::TupleEnd,
                serde_test::Token::StructEnd,
            ],
        )
    }
}
<commit_msg>Fix derive macro path<commit_after>//! Module for use with `#[serde(with = "...")]` to serialize a [`Uuid`]
//! as a `[u8; 16]`.
//!
//! [`Uuid`]: ../../struct.Uuid.html

/// Serializer for a [`Uuid`] into a `[u8; 16]`
///
/// [`Uuid`]: ../../struct.Uuid.html
pub fn serialize<S>(u: &crate::Uuid, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serde::Serialize::serialize(u.as_bytes(), serializer)
}

/// Deserializer from a `[u8; 16]` into a [`Uuid`]
///
/// [`Uuid`]: ../../struct.Uuid.html
pub fn deserialize<'de, D>(deserializer: D) -> Result<crate::Uuid, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let bytes: [u8; 16] = serde::Deserialize::deserialize(deserializer)?;

    Ok(crate::Uuid::from_bytes(bytes))
}

#[cfg(test)]
mod tests {

    use serde_test;

    #[test]
    fn test_serialize_compact() {
        #[derive(
            serde_derive::Serialize, Debug, serde_derive::Deserialize, PartialEq,
        )]
        struct UuidContainer {
            #[serde(with = "super")]
            u: crate::Uuid,
        }
        use serde_test::Configure;

        let uuid_bytes = b"F9168C5E-CEB2-4F";
        let container = UuidContainer {
            u: crate::Uuid::from_slice(uuid_bytes).unwrap(),
        };

        // more complex because of the struct wrapping the actual UUID
        // serialization
        serde_test::assert_tokens(
            &container.compact(),
            &[
                serde_test::Token::Struct {
                    name: "UuidContainer",
                    len: 1,
                },
                serde_test::Token::Str("u"),
                serde_test::Token::Tuple { len: 16 },
                serde_test::Token::U8(uuid_bytes[0]),
                serde_test::Token::U8(uuid_bytes[1]),
                serde_test::Token::U8(uuid_bytes[2]),
                serde_test::Token::U8(uuid_bytes[3]),
                serde_test::Token::U8(uuid_bytes[4]),
                serde_test::Token::U8(uuid_bytes[5]),
                serde_test::Token::U8(uuid_bytes[6]),
                serde_test::Token::U8(uuid_bytes[7]),
                serde_test::Token::U8(uuid_bytes[8]),
                serde_test::Token::U8(uuid_bytes[9]),
                serde_test::Token::U8(uuid_bytes[10]),
                serde_test::Token::U8(uuid_bytes[11]),
                serde_test::Token::U8(uuid_bytes[12]),
                serde_test::Token::U8(uuid_bytes[13]),
                serde_test::Token::U8(uuid_bytes[14]),
                serde_test::Token::U8(uuid_bytes[15]),
                serde_test::Token::TupleEnd,
                serde_test::Token::StructEnd,
            ],
        )
    }
}
<|endoftext|>


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

use crate::counters::{FETCHED_TRANSACTION, UNABLE_TO_FETCH_TRANSACTION};
use aptos_rest_client::{Client as RestClient, State, Transaction};
use futures::channel::mpsc;
use futures::{SinkExt, StreamExt};
use serde_json::Value;
use std::time::Duration;
use tokio::task::JoinHandle;
use url::Url;

// TODO: make this configurable
const RETRY_TIME_MILLIS: u64 = 1000;
const TRANSACTION_FETCH_BATCH_SIZE: u16 = 500;
const TRANSACTION_CHANNEL_SIZE: usize = 35;
const MAX_THREADS: usize = 10;
const MAX_RETRIES: usize = 5;

#[derive(Debug)]
pub struct Fetcher {
    client: RestClient,
    chain_id: u8,
    current_version: u64,
    highest_known_version: u64,
    transactions_sender: mpsc::Sender<Vec<Transaction>>,
}

impl Fetcher {
    pub fn new(
        client: RestClient,
        current_version: u64,
        transactions_sender: mpsc::Sender<Vec<Transaction>>,
    ) -> Self {
        Self {
            client,
            chain_id: 0,
            current_version,
            highest_known_version: current_version,
            transactions_sender,
        }
    }

    pub async fn fetch_ledger_info(&mut self) -> State {
        self.client
            .get_ledger_information()
            .await
            .expect("ledger info must be present")
            .into_inner()
    }

    pub async fn set_highest_known_version(&mut self) {
        let info = self.client.get_ledger_information().await;
        let res = info.unwrap();
        let state = res.state();
        self.highest_known_version = state.version;
        self.chain_id = state.chain_id;
    }

    pub async fn run(&mut self) {
        loop {
            if self.current_version >= self.highest_known_version {
                tokio::time::sleep(Duration::from_millis(200)).await;
                self.set_highest_known_version().await;
            }

            let num_missing = self.highest_known_version - self.current_version;
            let num_batches = std::cmp::min(
                (num_missing as f64 / TRANSACTION_FETCH_BATCH_SIZE as f64).ceil() as u64,
                MAX_THREADS as u64,
            ) as usize;
            let mut futures = vec![];
            for i in 0..num_batches {
                futures.push(fetch_nexts(
                    self.client.clone(),
                    self.current_version + (i as u64 * TRANSACTION_FETCH_BATCH_SIZE as u64),
                ));
            }
            let mut res: Vec<Vec<Transaction>> = futures::future::join_all(futures).await;
            res.sort_by(|a, b| {
                a.first()
                    .unwrap()
                    .version()
                    .unwrap()
                    .cmp(&b.first().unwrap().version().unwrap())
            });

            for batch in res {
                self.current_version = batch.last().unwrap().version().unwrap();
                self.transactions_sender.send(batch).await.unwrap();
            }
        }
    }
}

/// Fetches the next version based on its internal version counter
/// Under the hood, it fetches TRANSACTION_FETCH_BATCH_SIZE versions in bulk (when needed), and uses that buffer to feed out
/// In the event it can't fetch, it will keep retrying every RETRY_TIME_MILLIS ms
async fn fetch_nexts(client: RestClient, starting_version: u64) -> Vec<Transaction> {
    let mut retries = 0;
    while retries < MAX_RETRIES {
        retries += 1;

        let res = client
            .get_transactions(Some(starting_version), Some(TRANSACTION_FETCH_BATCH_SIZE))
            .await;

        match res {
            Ok(response) => {
                FETCHED_TRANSACTION.inc();
                return remove_null_bytes_from_txns(response.into_inner());
            }
            Err(err) => {
                let err_str = err.to_string();
                // If it's a 404, then we're all caught up; no need to increment the `UNABLE_TO_FETCH_TRANSACTION` counter
                if err_str.contains("404") {
                    aptos_logger::debug!(
                            "Could not fetch {} transactions starting at {}: all caught up. Will check again in {}ms.",
                            TRANSACTION_FETCH_BATCH_SIZE,
                            starting_version,
                            RETRY_TIME_MILLIS,
                        );
                }
                UNABLE_TO_FETCH_TRANSACTION.inc();
                aptos_logger::error!(
                    "Could not fetch {} transactions starting at {}, will retry in {}ms. Err: {:?}",
                    TRANSACTION_FETCH_BATCH_SIZE,
                    starting_version,
                    RETRY_TIME_MILLIS,
                    err
                );
            }
        }
        tokio::time::sleep(Duration::from_millis(RETRY_TIME_MILLIS)).await;
    }

    panic!(
        "Could not fetch {} transactions starting at {}!",
        TRANSACTION_FETCH_BATCH_SIZE, starting_version
    );
}

#[derive(Debug)]
pub struct TransactionFetcher {
    starting_version: u64,
    client: RestClient,
    fetcher_handle: Option<JoinHandle<()>>,
    transactions_sender: Option<mpsc::Sender<Vec<Transaction>>>,
    transaction_receiver: mpsc::Receiver<Vec<Transaction>>,
}

impl TransactionFetcher {
    pub fn new(node_url: Url, starting_version: Option<u64>) -> Self {
        let (transactions_sender, transaction_receiver) =
            mpsc::channel::<Vec<Transaction>>(TRANSACTION_CHANNEL_SIZE);

        let client = RestClient::new(node_url);

        Self {
            starting_version: starting_version.unwrap_or(0),
            client,
            fetcher_handle: None,
            transactions_sender: Some(transactions_sender),
            transaction_receiver,
        }
    }
}

#[async_trait::async_trait]
impl TransactionFetcherTrait for TransactionFetcher {
    /// Fetches the next batch based on its internal version counter
    /// Under the hood, it fetches TRANSACTION_FETCH_BATCH_SIZE versions in bulk (when needed), and uses that buffer to feed out
    /// In the event it can't fetch, it will keep retrying every RETRY_TIME_MILLIS ms
    async fn fetch_next_batch(&mut self) -> Vec<Transaction> {
        self.transaction_receiver.next().await.unwrap()
    }

    /// fetches one version; this used for error checking/repair/etc
    /// In the event it can't, it will keep retrying every RETRY_TIME_MILLIS ms
    async fn fetch_version(&self, version: u64) -> Transaction {
        loop {
            let res = self.client.get_transaction_by_version(version).await;
            match res {
                Ok(response) => {
                    FETCHED_TRANSACTION.inc();
                    return response.into_inner();
                }
                Err(err) => {
                    UNABLE_TO_FETCH_TRANSACTION.inc();
                    aptos_logger::error!(
                        "Could not fetch version {}, will retry in {}ms. Err: {:?}",
                        version,
                        RETRY_TIME_MILLIS,
                        err
                    );
                    tokio::time::sleep(Duration::from_millis(RETRY_TIME_MILLIS)).await;
                }
            };
        }
    }

    async fn fetch_ledger_info(&mut self) -> State {
        self.client
            .get_ledger_information()
            .await
            .expect("ledger info must be present")
            .into_inner()
    }

    async fn set_version(&mut self, version: u64) {
        if self.fetcher_handle.is_some() {
            panic!("TransactionFetcher already started!");
        }
        self.starting_version = version;
    }

    async fn start(&mut self) {
        if self.fetcher_handle.is_some() {
            panic!("TransactionFetcher already started!");
        }
        let client = self.client.clone();
        let transactions_sender = self.transactions_sender.take().unwrap();
        let starting_version = self.starting_version;
        let fetcher_handle = tokio::spawn(async move {
            let mut fetcher = Fetcher::new(client, starting_version, transactions_sender);
            fetcher.run().await;
        });
        self.fetcher_handle = Some(fetcher_handle);
    }
}

pub fn string_null_byte_replacement(value: &mut str) -> String {
    value.replace('\u{0000}', "").replace("\\u0000", "")
}

pub fn recurse_remove_null_bytes_from_json(sub_json: &mut Value) {
    match sub_json {
        Value::Array(array) => {
            for item in array {
                recurse_remove_null_bytes_from_json(item);
            }
        }
        Value::Object(object) => {
            for (_key, value) in object {
                recurse_remove_null_bytes_from_json(value);
            }
        }
        Value::String(str) => {
            if !str.is_empty() {
                let replacement = string_null_byte_replacement(str);
                *str = replacement;
            }
        }
        _ => {}
    }
}

pub fn remove_null_bytes_from_txns(txns: Vec<Transaction>) -> Vec<Transaction> {
    txns.iter()
        .map(|txn| {
            let mut txn_json = serde_json::to_value(txn).unwrap();
            recurse_remove_null_bytes_from_json(&mut txn_json);
            serde_json::from_value::<Transaction>(txn_json).unwrap()
        })
        .collect::<Vec<Transaction>>()
}

/// For mocking TransactionFetcher in tests
#[async_trait::async_trait]
pub trait TransactionFetcherTrait: Send + Sync {
    async fn fetch_next_batch(&mut self) -> Vec<Transaction>;

    async fn fetch_version(&self, version: u64) -> Transaction;

    async fn fetch_ledger_info(&mut self) -> State;

    async fn set_version(&mut self, version: u64);

    async fn start(&mut self);
}
<commit_msg>[indexer] Allow for failures in ledger info<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::counters::{FETCHED_TRANSACTION, UNABLE_TO_FETCH_TRANSACTION};
use aptos_rest_client::{Client as RestClient, State, Transaction};
use futures::channel::mpsc;
use futures::{SinkExt, StreamExt};
use serde_json::Value;
use std::time::Duration;
use tokio::task::JoinHandle;
use url::Url;

// TODO: make this configurable
const RETRY_TIME_MILLIS: u64 = 1000;
const TRANSACTION_FETCH_BATCH_SIZE: u16 = 500;
const TRANSACTION_CHANNEL_SIZE: usize = 35;
const MAX_THREADS: usize = 10;
const MAX_RETRIES: usize = 5;

#[derive(Debug)]
pub struct Fetcher {
    client: RestClient,
    chain_id: u8,
    current_version: u64,
    highest_known_version: u64,
    transactions_sender: mpsc::Sender<Vec<Transaction>>,
}

impl Fetcher {
    pub fn new(
        client: RestClient,
        current_version: u64,
        transactions_sender: mpsc::Sender<Vec<Transaction>>,
    ) -> Self {
        Self {
            client,
            chain_id: 0,
            current_version,
            highest_known_version: current_version,
            transactions_sender,
        }
    }

    pub async fn set_highest_known_version(&mut self) {
        let info = self.client.get_ledger_information().await;
        let res = info.unwrap();
        let state = res.state();
        self.highest_known_version = state.version;
        self.chain_id = state.chain_id;
    }

    pub async fn run(&mut self) {
        loop {
            if self.current_version >= self.highest_known_version {
                tokio::time::sleep(Duration::from_millis(200)).await;
                self.set_highest_known_version().await;
            }

            let num_missing = self.highest_known_version - self.current_version;
            let num_batches = std::cmp::min(
                (num_missing as f64 / TRANSACTION_FETCH_BATCH_SIZE as f64).ceil() as u64,
                MAX_THREADS as u64,
            ) as usize;
            let mut futures = vec![];
            for i in 0..num_batches {
                futures.push(fetch_nexts(
                    self.client.clone(),
                    self.current_version + (i as u64 * TRANSACTION_FETCH_BATCH_SIZE as u64),
                ));
            }
            let mut res: Vec<Vec<Transaction>> = futures::future::join_all(futures).await;
            res.sort_by(|a, b| {
                a.first()
                    .unwrap()
                    .version()
                    .unwrap()
                    .cmp(&b.first().unwrap().version().unwrap())
            });

            for batch in res {
                self.current_version = batch.last().unwrap().version().unwrap();
                self.transactions_sender.send(batch).await.unwrap();
            }
        }
    }
}

/// Fetches the next version based on its internal version counter
/// Under the hood, it fetches TRANSACTION_FETCH_BATCH_SIZE versions in bulk (when needed), and uses that buffer to feed out
/// In the event it can't fetch, it will keep retrying every RETRY_TIME_MILLIS ms
async fn fetch_nexts(client: RestClient, starting_version: u64) -> Vec<Transaction> {
    let mut retries = 0;
    while retries < MAX_RETRIES {
        retries += 1;

        let res = client
            .get_transactions(Some(starting_version), Some(TRANSACTION_FETCH_BATCH_SIZE))
            .await;

        match res {
            Ok(response) => {
                FETCHED_TRANSACTION.inc();
                return remove_null_bytes_from_txns(response.into_inner());
            }
            Err(err) => {
                let err_str = err.to_string();
                // If it's a 404, then we're all caught up; no need to increment the `UNABLE_TO_FETCH_TRANSACTION` counter
                if err_str.contains("404") {
                    aptos_logger::debug!(
                            "Could not fetch {} transactions starting at {}: all caught up. Will check again in {}ms.",
                            TRANSACTION_FETCH_BATCH_SIZE,
                            starting_version,
                            RETRY_TIME_MILLIS,
                        );
                }
                UNABLE_TO_FETCH_TRANSACTION.inc();
                aptos_logger::error!(
                    "Could not fetch {} transactions starting at {}, will retry in {}ms. Err: {:?}",
                    TRANSACTION_FETCH_BATCH_SIZE,
                    starting_version,
                    RETRY_TIME_MILLIS,
                    err
                );
            }
        }
        tokio::time::sleep(Duration::from_millis(RETRY_TIME_MILLIS)).await;
    }

    panic!(
        "Could not fetch {} transactions starting at {}!",
        TRANSACTION_FETCH_BATCH_SIZE, starting_version
    );
}

#[derive(Debug)]
pub struct TransactionFetcher {
    starting_version: u64,
    client: RestClient,
    fetcher_handle: Option<JoinHandle<()>>,
    transactions_sender: Option<mpsc::Sender<Vec<Transaction>>>,
    transaction_receiver: mpsc::Receiver<Vec<Transaction>>,
}

impl TransactionFetcher {
    pub fn new(node_url: Url, starting_version: Option<u64>) -> Self {
        let (transactions_sender, transaction_receiver) =
            mpsc::channel::<Vec<Transaction>>(TRANSACTION_CHANNEL_SIZE);

        let client = RestClient::new(node_url);

        Self {
            starting_version: starting_version.unwrap_or(0),
            client,
            fetcher_handle: None,
            transactions_sender: Some(transactions_sender),
            transaction_receiver,
        }
    }
}

#[async_trait::async_trait]
impl TransactionFetcherTrait for TransactionFetcher {
    /// Fetches the next batch based on its internal version counter
    /// Under the hood, it fetches TRANSACTION_FETCH_BATCH_SIZE versions in bulk (when needed), and uses that buffer to feed out
    /// In the event it can't fetch, it will keep retrying every RETRY_TIME_MILLIS ms
    async fn fetch_next_batch(&mut self) -> Vec<Transaction> {
        self.transaction_receiver.next().await.unwrap()
    }

    /// fetches one version; this used for error checking/repair/etc
    /// In the event it can't, it will keep retrying every RETRY_TIME_MILLIS ms
    async fn fetch_version(&self, version: u64) -> Transaction {
        loop {
            let res = self.client.get_transaction_by_version(version).await;
            match res {
                Ok(response) => {
                    FETCHED_TRANSACTION.inc();
                    return response.into_inner();
                }
                Err(err) => {
                    UNABLE_TO_FETCH_TRANSACTION.inc();
                    aptos_logger::error!(
                        "Could not fetch version {}, will retry in {}ms. Err: {:?}",
                        version,
                        RETRY_TIME_MILLIS,
                        err
                    );
                    tokio::time::sleep(Duration::from_millis(RETRY_TIME_MILLIS)).await;
                }
            };
        }
    }

    async fn fetch_ledger_info(&mut self) -> State {
        let mut retries = 0;
        while retries < MAX_RETRIES {
            retries += 1;
            match self.client.get_ledger_information().await {
                Ok(inner) => return inner.into_inner(),
                Err(err) => {
                    aptos_logger::error!(
                        "Failed to get ledger info, will retry in {}ms. Err: {:?}",
                        RETRY_TIME_MILLIS,
                        err
                    );
                    tokio::time::sleep(Duration::from_secs(RETRY_TIME_MILLIS)).await
                }
            }
        }
        panic!("Failed to get ledger info.",);
    }

    async fn set_version(&mut self, version: u64) {
        if self.fetcher_handle.is_some() {
            panic!("TransactionFetcher already started!");
        }
        self.starting_version = version;
    }

    async fn start(&mut self) {
        if self.fetcher_handle.is_some() {
            panic!("TransactionFetcher already started!");
        }
        let client = self.client.clone();
        let transactions_sender = self.transactions_sender.take().unwrap();
        let starting_version = self.starting_version;
        let fetcher_handle = tokio::spawn(async move {
            let mut fetcher = Fetcher::new(client, starting_version, transactions_sender);
            fetcher.run().await;
        });
        self.fetcher_handle = Some(fetcher_handle);
    }
}

pub fn string_null_byte_replacement(value: &mut str) -> String {
    value.replace('\u{0000}', "").replace("\\u0000", "")
}

pub fn recurse_remove_null_bytes_from_json(sub_json: &mut Value) {
    match sub_json {
        Value::Array(array) => {
            for item in array {
                recurse_remove_null_bytes_from_json(item);
            }
        }
        Value::Object(object) => {
            for (_key, value) in object {
                recurse_remove_null_bytes_from_json(value);
            }
        }
        Value::String(str) => {
            if !str.is_empty() {
                let replacement = string_null_byte_replacement(str);
                *str = replacement;
            }
        }
        _ => {}
    }
}

pub fn remove_null_bytes_from_txns(txns: Vec<Transaction>) -> Vec<Transaction> {
    txns.iter()
        .map(|txn| {
            let mut txn_json = serde_json::to_value(txn).unwrap();
            recurse_remove_null_bytes_from_json(&mut txn_json);
            serde_json::from_value::<Transaction>(txn_json).unwrap()
        })
        .collect::<Vec<Transaction>>()
}

/// For mocking TransactionFetcher in tests
#[async_trait::async_trait]
pub trait TransactionFetcherTrait: Send + Sync {
    async fn fetch_next_batch(&mut self) -> Vec<Transaction>;

    async fn fetch_version(&self, version: u64) -> Transaction;

    async fn fetch_ledger_info(&mut self) -> State;

    async fn set_version(&mut self, version: u64);

    async fn start(&mut self);
}
<|endoftext|>


<commit_before>//! Top-level tests on regression scripts in `tests`.
#![allow(non_upper_case_globals)]

extern crate hoice ;

use std::fs::read_dir ;
use std::fs::OpenOptions ;

use hoice::common::* ;
use hoice::read_and_work ;

static sat_files_dir: & str = "rsc/sat" ;
static unsat_files_dir: & str = "rsc/unsat" ;
static err_files_dir: & str = "rsc/error" ;

macro_rules! run {
  ($f:expr) => (
    if let Err(e) = $f {
      println!("Error:") ;
      for e in e.iter() {
        let mut pref = "> " ;
        for line in format!("{}", e).lines() {
          println!("{}{}", pref, line) ;
          pref = "  "
        }
      }
      panic!("failure")
    }
  ) ;
}

#[test]
fn sat() { run!( run_sat() ) }

#[test]
fn sat_ackermann() { run!( run_sat_on("rsc/sat/long/Ackermann00.smt2") ) }

#[test]
fn sat_file() { run!( run_sat_on("rsc/sat/long/file.smt2") ) }

#[test]
fn sat_rec_simpl() {
  run!( run_sat_on("rsc/sat/long/recursive_simplifications.smt2") )
}

#[test]
fn unsat() { run!( run_unsat() ) }

#[test]
fn err() { run!( run_err() ) }

macro_rules! map_err {
  ($e:expr, $msg:expr) => (
    $e.map_err( |e| format!("{}:\n{}", $msg, e) ) ?
  ) ;
  ($e:expr, $($tt:tt)*) => (
    $e.map_err( |e| format!("{}:\n{}", format!($($tt)*), e) ) ?
  ) ;
}

fn run_err() -> Res<()> {

  let files = map_err!(
    read_dir(err_files_dir), format!("while reading `{}`", err_files_dir)
  ) ;

  for entry in files {
    let entry = map_err!(
      entry, "while reading entry"
    ) ;
    let file_name = format!("{}", entry.file_name().to_string_lossy()) ;
    if map_err!(
      entry.file_type(), "while reading entry (file type of `{}`)", file_name
    ).is_file() {
      println!("looking at `{}`", file_name) ;
      let file = OpenOptions::new().read(true).open(entry.path()).chain_err(
        || format!( "while opening file {}", file_name )
      ) ? ;
      match read_and_work(file, true, true, true) {
        Err(e) => println!("got {}", e),
        Ok((model, _)) => return Err(
          format!(
            "expected error, got {}",
            if model.is_some() { "sat" } else { "unsat" }
          ).into()
        ),
      }
    }
  }
  Ok(())
}

fn run_sat() -> Res<()> {

  let files = map_err!(
    read_dir(sat_files_dir), format!("while reading `{}`", sat_files_dir)
  ) ;

  for entry in files {
    let entry = map_err!(
      entry, "while reading entry"
    ) ;
    let file_name = format!("{}", entry.file_name().to_string_lossy()) ;
    if map_err!(
      entry.file_type(), "while reading entry (file type of `{}`)", file_name
    ).is_file() {
      run_sat_on(& entry.path()) ?
    }
  }

  Ok(())
}

fn run_sat_on<P: AsRef<::std::path::Path> + ?Sized>(path: & P) -> Res<()> {
  let file_name = path.as_ref() ;
  println!("looking at `{}`", file_name.display()) ;
  let file = OpenOptions::new().read(true).open(file_name).chain_err(
    || format!( "while opening file {}", file_name.display() )
  ) ? ;
  let (model, instance) = read_and_work(file, true, true, true).chain_err(
    || "while reading file and getting model"
  ) ? ;
  if let Some(model) = model {
    let mut buff: Vec<u8> = vec![] ;
    instance.write_model(& model, & mut buff).chain_err(
      || "while writing model"
    ) ? ;
    let buff = map_err!(
      String::from_utf8(buff), "converting model from bytes to utf8"
    ) ;
    ::hoice::check::do_it_from_str(file_name, & buff).chain_err(
      || "while checking model"
    ) ? ;
    println!("- is okay") ;
    Ok(())
  } else {
    Err(
      format!( "got unsat on `{}`, expected sat", file_name.display() ).into()
    )
  }
}

fn run_unsat() -> Res<()> {

  let files = map_err!(
    read_dir(unsat_files_dir), format!("while reading `{}`", unsat_files_dir)
  ) ;

  for entry in files {
    let entry = map_err!(
      entry, "while reading entry"
    ) ;
    let file_name = format!("{}", entry.file_name().to_string_lossy()) ;
    if map_err!(
      entry.file_type(), "while reading entry (file type of `{}`)", file_name
    ).is_file() {
      println!("looking at `{}`", file_name) ;
      let file = OpenOptions::new().read(true).open(entry.path()).chain_err(
        || format!( "while opening file {}", file_name )
      ) ? ;
      let (model, instance) = read_and_work(file, true, true, true) ? ;
      if let Some(model) = model {
        println!("sat") ;
        instance.write_model(& model, & mut ::std::io::stdout()) ? ;
        println!("") ;
        return Err(
          format!( "got sat on `{}`, expected unsat", file_name ).into()
        )
      } else {
        println!("- is okay")
      }
    }
  }

  Ok(())

}<commit_msg>deactivated some tests on windows<commit_after>//! Top-level tests on regression scripts in `tests`.
#![allow(non_upper_case_globals)]

extern crate hoice ;

use std::fs::read_dir ;
use std::fs::OpenOptions ;

use hoice::common::* ;
use hoice::read_and_work ;

static sat_files_dir: & str = "rsc/sat" ;
static unsat_files_dir: & str = "rsc/unsat" ;
static err_files_dir: & str = "rsc/error" ;

macro_rules! run {
  ($f:expr) => (
    if let Err(e) = $f {
      println!("Error:") ;
      for e in e.iter() {
        let mut pref = "> " ;
        for line in format!("{}", e).lines() {
          println!("{}{}", pref, line) ;
          pref = "  "
        }
      }
      panic!("failure")
    }
  ) ;
}

#[test]
fn sat() { run!( run_sat() ) }

#[cfg( not(windows) )]
#[test]
fn sat_ackermann() { run!( run_sat_on("rsc/sat/long/Ackermann00.smt2") ) }

#[cfg( not(windows) )]
#[test]
fn sat_file() { run!( run_sat_on("rsc/sat/long/file.smt2") ) }

#[cfg( not(windows) )]
#[test]
fn sat_rec_simpl() {
  run!( run_sat_on("rsc/sat/long/recursive_simplifications.smt2") )
}

#[test]
fn unsat() { run!( run_unsat() ) }

#[test]
fn err() { run!( run_err() ) }

macro_rules! map_err {
  ($e:expr, $msg:expr) => (
    $e.map_err( |e| format!("{}:\n{}", $msg, e) ) ?
  ) ;
  ($e:expr, $($tt:tt)*) => (
    $e.map_err( |e| format!("{}:\n{}", format!($($tt)*), e) ) ?
  ) ;
}

fn run_err() -> Res<()> {

  let files = map_err!(
    read_dir(err_files_dir), format!("while reading `{}`", err_files_dir)
  ) ;

  for entry in files {
    let entry = map_err!(
      entry, "while reading entry"
    ) ;
    let file_name = format!("{}", entry.file_name().to_string_lossy()) ;
    if map_err!(
      entry.file_type(), "while reading entry (file type of `{}`)", file_name
    ).is_file() {
      println!("looking at `{}`", file_name) ;
      let file = OpenOptions::new().read(true).open(entry.path()).chain_err(
        || format!( "while opening file {}", file_name )
      ) ? ;
      match read_and_work(file, true, true, true) {
        Err(e) => println!("got {}", e),
        Ok((model, _)) => return Err(
          format!(
            "expected error, got {}",
            if model.is_some() { "sat" } else { "unsat" }
          ).into()
        ),
      }
    }
  }
  Ok(())
}

fn run_sat() -> Res<()> {

  let files = map_err!(
    read_dir(sat_files_dir), format!("while reading `{}`", sat_files_dir)
  ) ;

  for entry in files {
    let entry = map_err!(
      entry, "while reading entry"
    ) ;
    let file_name = format!("{}", entry.file_name().to_string_lossy()) ;
    if map_err!(
      entry.file_type(), "while reading entry (file type of `{}`)", file_name
    ).is_file() {
      run_sat_on(& entry.path()) ?
    }
  }

  Ok(())
}

fn run_sat_on<P: AsRef<::std::path::Path> + ?Sized>(path: & P) -> Res<()> {
  let file_name = path.as_ref() ;
  println!("looking at `{}`", file_name.display()) ;
  let file = OpenOptions::new().read(true).open(file_name).chain_err(
    || format!( "while opening file {}", file_name.display() )
  ) ? ;
  let (model, instance) = read_and_work(file, true, true, true).chain_err(
    || "while reading file and getting model"
  ) ? ;
  if let Some(model) = model {
    let mut buff: Vec<u8> = vec![] ;
    instance.write_model(& model, & mut buff).chain_err(
      || "while writing model"
    ) ? ;
    let buff = map_err!(
      String::from_utf8(buff), "converting model from bytes to utf8"
    ) ;
    ::hoice::check::do_it_from_str(file_name, & buff).chain_err(
      || "while checking model"
    ) ? ;
    println!("- is okay") ;
    Ok(())
  } else {
    Err(
      format!( "got unsat on `{}`, expected sat", file_name.display() ).into()
    )
  }
}

fn run_unsat() -> Res<()> {

  let files = map_err!(
    read_dir(unsat_files_dir), format!("while reading `{}`", unsat_files_dir)
  ) ;

  for entry in files {
    let entry = map_err!(
      entry, "while reading entry"
    ) ;
    let file_name = format!("{}", entry.file_name().to_string_lossy()) ;
    if map_err!(
      entry.file_type(), "while reading entry (file type of `{}`)", file_name
    ).is_file() {
      println!("looking at `{}`", file_name) ;
      let file = OpenOptions::new().read(true).open(entry.path()).chain_err(
        || format!( "while opening file {}", file_name )
      ) ? ;
      let (model, instance) = read_and_work(file, true, true, true) ? ;
      if let Some(model) = model {
        println!("sat") ;
        instance.write_model(& model, & mut ::std::io::stdout()) ? ;
        println!("") ;
        return Err(
          format!( "got sat on `{}`, expected unsat", file_name ).into()
        )
      } else {
        println!("- is okay")
      }
    }
  }

  Ok(())

}<|endoftext|>


<commit_before>extern crate reqwest;

use model::card::{Card, CardPage, Variation};
use self::reqwest::Url;
use std::collections::HashMap;

macro_rules! get_card {
    ($query: expr, $query_type: ty) => (
        {
            let mut resp = reqwest::get($query)?;
            println!("Query: {:?}", $query);
            println!("Status: {:?}", resp.status());
            match resp.json::<$query_type>() {
                Err(e) => Err(e),
                Ok(card) => Ok(card)
            }
        }
    )
}

pub struct QueryBuilder(pub HashMap<String, String>);

impl QueryBuilder {
    pub fn new() -> QueryBuilder {
        QueryBuilder(HashMap::new())
    }

    pub fn as_url(&self, base_url: &str) -> Result<Url, reqwest::UrlError> {
        Url::parse_with_params(base_url, &self.0)
    }

    pub fn lang<'a>(&'a mut self, language: &str) -> &'a mut Self {
        self.0.insert(String::from("lang"), String::from(language));
        self
    }

    pub fn card_name<'a>(&'a mut self, card_name: &str) -> &'a mut Self {
        self.0.insert(String::from("name"), String::from(card_name));
        self
    }

    pub fn with_arg<'a>(&'a mut self, param: &str, value: &str) -> &'a mut Self {
        self.0.insert(String::from(param), String::from(value));
        self
    }
}

pub struct Client { }

impl Client {
    fn get_query_card_by_name(card_name: &str) -> Result<Url, reqwest::UrlError> {
        QueryBuilder::new()
            .card_name(card_name)
            .as_url("https://api.gwentapi.com/v0/cards/")
    }

    pub fn get_card_by_name(card_name: &str) -> reqwest::Result<Card> {
        let search_query = Self::get_query_card_by_name(card_name).unwrap();
        let mut search_result = get_card!(search_query.as_str(), CardPage).unwrap();
        let card_uri = if search_result.count >= 1 {
                let uri = search_result.results.pop().unwrap();
                uri.href.clone()
            }
            else {
                String::new()
            };
        get_card!(card_uri.as_str(), Card)
    }

    pub fn get_card_default_art(card: &Card) -> reqwest::Result<Variation> {
        let search_query = card.variations[0].href.clone();
        get_card!(search_query.as_str(), Variation)
    }
}<commit_msg>Adds basic documentation.<commit_after>extern crate reqwest;

use model::card::{Card, CardPage, Variation};
use self::reqwest::Url;
use std::collections::HashMap;

macro_rules! get_card {
    ($query: expr, $query_type: ty) => (
        {
            let mut resp = reqwest::get($query)?;
            println!("Query: {:?}", $query);
            println!("Status: {:?}", resp.status());
            match resp.json::<$query_type>() {
                Err(e) => Err(e),
                Ok(card) => Ok(card)
            }
        }
    )
}

/// Structure used to construct an url to query the gwent api.
///
/// Functions on the QueryBuilder are chainable, provided it has been
/// initialized first.
///
/// Links to the [gwent api](https://gwentapi.com/) and its [documentation](https://gwentapi.com/swagger/index.html).
pub struct QueryBuilder(pub HashMap<String, String>);

impl QueryBuilder {
    /// Constructs a new QueryBuilder. It is necessary to do so before chaining other operations.
    pub fn new() -> QueryBuilder {
        QueryBuilder(HashMap::new())
    }

    /// Returns an Url with all parameters set.
    ///
    /// # Arguments
    ///
    /// * `base_url` - the url targetted by the request
    ///
    /// # Examples
    ///
    /// ```
    /// use reqwest::Url;
    ///
    /// let url = QueryBuilder::new()
    ///     .card_name("Alchemist")
    ///     .as_url("https://api.gwentapi.com/v0/cards/");
    /// ```
    pub fn as_url(&self, base_url: &str) -> Result<Url, reqwest::UrlError> {
        Url::parse_with_params(base_url, &self.0)
    }

    /// Sets the language of the returned response.
    ///
    /// # Arguments
    ///
    /// * `language` - string ∈ {en-US, de-DE, es-ES, es-MX, fr-FR, it-IT, ja-JP, pl-PL, pt-BR, ru-RU}
    ///
    /// # Examples
    ///
    /// ```
    /// use reqwest::Url;
    ///
    /// let url = QueryBuilder::new()
    ///     .lang("fr-FR")
    ///     .as_url("https://api.gwentapi.com/v0/cards/bLkeCcb0VeaNNS2J73VLVA");
    /// ```
    pub fn lang<'a>(&'a mut self, language: &str) -> &'a mut Self {
        self.0.insert(String::from("lang"), String::from(language));
        self
    }

    /// Sets the name of the searched card.
    ///
    /// # Arguments
    ///
    /// * `card_name` - name of the card
    ///
    /// # Examples
    ///
    /// ```
    /// let url = QueryBuilder::new()
    ///     .card_name("Geralt: Igni")
    ///     .as_url("https://api.gwentapi.com/v0/cards/");
    /// ```
    ///
    /// # Errors
    ///
    /// Not available on certain API requests.
    ///
    /// Will fail with:
    ///
    /// * GET /v0/cards
    pub fn card_name<'a>(&'a mut self, card_name: &str) -> &'a mut Self {
        self.0.insert(String::from("name"), String::from(card_name));
        self
    }

    /// Generic parameter setting.
    ///
    /// # Arguments
    ///
    /// * `param` - name of the request parameter
    /// * `value` - value of the request parameter
    pub fn with_arg<'a>(&'a mut self, param: &str, value: &str) -> &'a mut Self {
        self.0.insert(String::from(param), String::from(value));
        self
    }
}

/// Gateway offering specific functions through the gwent api.
pub struct Client { }

impl Client {
    fn get_query_card_by_name(card_name: &str) -> Result<Url, reqwest::UrlError> {
        QueryBuilder::new()
            .card_name(card_name)
            .as_url("https://api.gwentapi.com/v0/cards/")
    }

    /// Retrieves a Card object based on its name.
    ///
    /// # Arguments
    ///
    /// * `card_name` - the name of the desired card
    ///
    /// # Errors
    ///
    /// Will fail if the provided string doesn't correspons to any card in the API.
    /// All pattern matching and partial recognition is handled by the server.
    pub fn get_card_by_name(card_name: &str) -> reqwest::Result<Card> {
        let search_query = Self::get_query_card_by_name(card_name).unwrap();
        let mut search_result = get_card!(search_query.as_str(), CardPage).unwrap();
        let card_uri = if search_result.count >= 1 {
                let uri = search_result.results.pop().unwrap();
                uri.href.clone()
            }
            else {
                String::new()
            };
        get_card!(card_uri.as_str(), Card)
    }

    /// Retrieves the default Variation object of a given card.
    ///
    /// This object contains a link to the art representing the card in the game.
    ///
    /// # Arguments
    ///
    /// * `card`: a reference to the Card
    pub fn get_card_default_art(card: &Card) -> reqwest::Result<Variation> {
        let search_query = card.variations[0].href.clone();
        get_card!(search_query.as_str(), Variation)
    }
}<|endoftext|>


<commit_before>//! An IRC transport that wraps an IRC-framed stream to provide a number of features including
//! automatic PING replies, automatic sending of PINGs, and message rate-limiting.
use std::sync::{Arc, RwLock, RwLockReadGuard};
use std::time::{Duration, Instant};

use futures::{Async, AsyncSink, Future, Poll, Sink, StartSend, Stream};
use chrono::prelude::*;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::Framed;
use tokio_timer;
use tokio_timer::{Interval, Sleep, Timer};

use error;
use client::data::Config;
use proto::{Command, IrcCodec, Message};

/// An IRC transport that handles core functionality.
pub struct IrcTransport<T>
where
    T: AsyncRead + AsyncWrite,
{
    inner: Framed<T, IrcCodec>,
    burst_timer: Timer,
    rolling_burst_window: Sleep,
    burst_window_length: u64,
    max_burst_messages: u64,
    current_burst_messages: u64,
    ping_timer: Interval,
    ping_timeout: u64,
    last_ping_data: String,
    last_ping_sent: Instant,
    last_pong_received: Instant,
}

impl<T> IrcTransport<T>
where
    T: AsyncRead + AsyncWrite,
{
    /// Creates a new `IrcTransport` from the given IRC stream.
    pub fn new(config: &Config, inner: Framed<T, IrcCodec>) -> IrcTransport<T> {
        let timer = tokio_timer::wheel().build();
        let burst = tokio_timer::wheel().build();
        let burst_window = burst.sleep(Duration::from_secs(config.burst_window_length() as u64));
        IrcTransport {
            inner: inner,
            burst_timer: burst,
            rolling_burst_window: burst_window,
            burst_window_length: config.burst_window_length() as u64,
            max_burst_messages: config.max_messages_in_burst() as u64,
            current_burst_messages: 0,
            ping_timer: timer.interval(Duration::from_secs(config.ping_time() as u64)),
            ping_timeout: config.ping_timeout() as u64,
            last_ping_data: String::new(),
            last_ping_sent: Instant::now(),
            last_pong_received: Instant::now(),
        }
    }

    /// Gets the inner stream underlying the `IrcTransport`.
    pub fn into_inner(self) -> Framed<T, IrcCodec> {
        self.inner
    }

    fn ping_timed_out(&self) -> bool {
        if self.last_pong_received < self.last_ping_sent {
            self.last_ping_sent.elapsed().as_secs() >= self.ping_timeout
        } else {
            false
        }
    }

    fn send_ping(&mut self) -> error::Result<()> {
        self.last_ping_sent = Instant::now();
        self.last_ping_data = format!("{}", Local::now().timestamp());
        let data = self.last_ping_data.clone();
        let result = self.start_send(Command::PING(data, None).into())?;
        assert!(result.is_ready());
        self.poll_complete()?;
        Ok(())
    }
}

impl<T> Stream for IrcTransport<T>
where
    T: AsyncRead + AsyncWrite,
{
    type Item = Message;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if self.ping_timed_out() {
            self.close()?;
            return Err(error::ErrorKind::PingTimeout.into())
        }

        let timer_poll = self.ping_timer.poll()?;
        let inner_poll = self.inner.poll()?;

        match (inner_poll, timer_poll) {
            (Async::NotReady, Async::NotReady) => Ok(Async::NotReady),
            (Async::NotReady, Async::Ready(msg)) => {
                assert!(msg.is_some());
                self.send_ping()?;
                Ok(Async::NotReady)
            }
            (Async::Ready(None), _) => Ok(Async::Ready(None)),
            (Async::Ready(Some(msg)), _) => {
                match timer_poll {
                    Async::Ready(msg) => {
                        assert!(msg.is_some());
                        self.send_ping()?;
                    }
                    Async::NotReady => (),
                }

                match msg.command {
                    // Automatically respond to PINGs from the server.
                    Command::PING(ref data, _) => {
                        let result = self.start_send(Command::PONG(data.to_owned(), None).into())?;
                        assert!(result.is_ready());
                        self.poll_complete()?;
                    }
                    // Check PONG responses from the server.
                    Command::PONG(ref data, None) |
                    Command::PONG(_, Some(ref data)) => {
                        if self.last_ping_data == &data[..] {
                            self.last_pong_received = Instant::now();
                        }
                    }
                    _ => (),
                }

                Ok(Async::Ready(Some(msg)))
            }
        }
    }
}

impl<T> Sink for IrcTransport<T>
where
    T: AsyncRead + AsyncWrite,
{
    type SinkItem = Message;
    type SinkError = error::Error;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        if self.ping_timed_out() {
            self.close()?;
            Err(error::ErrorKind::PingTimeout.into())
        } else {
            // Check if the last rolling window has ended.
            match self.rolling_burst_window.poll()? {
                Async::NotReady => (),
                Async::Ready(()) => {
                    // Since it has ended, we reset the message count and start a new window.
                    self.current_burst_messages = 0;
                    self.rolling_burst_window = self.burst_timer.sleep(Duration::from_secs(
                        self.burst_window_length
                    ));
                }
            }

            // Throttling if too many messages have been sent recently.
            if self.current_burst_messages >= self.max_burst_messages {
                // When throttled, we know we need to finish sending what's already queued up.
                self.poll_complete()?;
                return Ok(AsyncSink::NotReady(item))
            }

            match self.inner.start_send(item)? {
                AsyncSink::NotReady(item) => Ok(AsyncSink::NotReady(item)),
                AsyncSink::Ready => {
                    self.current_burst_messages += 1;
                    Ok(AsyncSink::Ready)
                }
            }
        }
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        if self.ping_timed_out() {
            self.close()?;
            Err(error::ErrorKind::PingTimeout.into())
        } else {
            Ok(self.inner.poll_complete()?)
        }
    }

    fn close(&mut self) -> Poll<(), Self::SinkError> {
        self.inner.close()
    }
}

/// A view of the logs from a particular `Logged` transport.
#[derive(Clone, Debug)]
pub struct LogView {
    sent: Arc<RwLock<Vec<Message>>>,
    received: Arc<RwLock<Vec<Message>>>,
}

impl LogView {
    /// Gets a read guard for all the messages sent on the transport.
    pub fn sent(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
        self.sent.read().map_err(
            |_| error::ErrorKind::PoisonedLog.into(),
        )
    }

    /// Gets a read guard for all the messages received on the transport.
    pub fn received(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
        self.received.read().map_err(
            |_| error::ErrorKind::PoisonedLog.into(),
        )
    }
}

/// A logged version of the `IrcTransport` that records all sent and received messages.
/// Note: this will introduce some performance overhead by cloning all messages.
pub struct Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    inner: IrcTransport<T>,
    view: LogView,
}

impl<T> Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    /// Wraps the given `IrcTransport` in logging.
    pub fn wrap(inner: IrcTransport<T>) -> Logged<T> {
        Logged {
            inner: inner,
            view: LogView {
                sent: Arc::new(RwLock::new(vec![])),
                received: Arc::new(RwLock::new(vec![])),
            },
        }
    }

    /// Gets a view of the logging for this transport.
    pub fn view(&self) -> LogView {
        self.view.clone()
    }
}

impl<T> Stream for Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    type Item = Message;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match try_ready!(self.inner.poll()) {
            Some(msg) => {
                let recv: error::Result<_> = self.view.received.write().map_err(|_| {
                    error::ErrorKind::PoisonedLog.into()
                });
                recv?.push(msg.clone());
                Ok(Async::Ready(Some(msg)))
            }
            None => Ok(Async::Ready(None)),
        }
    }
}

impl<T> Sink for Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    type SinkItem = Message;
    type SinkError = error::Error;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        let res = self.inner.start_send(item.clone())?;
        let sent: error::Result<_> = self.view.sent.write().map_err(|_| {
            error::ErrorKind::PoisonedLog.into()
        });
        sent?.push(item);
        Ok(res)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        Ok(self.inner.poll_complete()?)
    }
}
<commit_msg>Changed throttling window to be properly rolling.<commit_after>//! An IRC transport that wraps an IRC-framed stream to provide a number of features including
//! automatic PING replies, automatic sending of PINGs, and message rate-limiting.
use std::collections::VecDeque;
use std::sync::{Arc, RwLock, RwLockReadGuard};
use std::time::{Duration, Instant};

use futures::{Async, AsyncSink, Future, Poll, Sink, StartSend, Stream};
use chrono::prelude::*;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::Framed;
use tokio_timer;
use tokio_timer::{Interval, Sleep, Timer};

use error;
use client::data::Config;
use proto::{Command, IrcCodec, Message};

/// An IRC transport that handles core functionality.
pub struct IrcTransport<T>
where
    T: AsyncRead + AsyncWrite,
{
    inner: Framed<T, IrcCodec>,
    burst_timer: Timer,
    rolling_burst_window: VecDeque<Sleep>,
    burst_window_length: u64,
    max_burst_messages: u64,
    current_burst_messages: u64,
    ping_timer: Interval,
    ping_timeout: u64,
    last_ping_data: String,
    last_ping_sent: Instant,
    last_pong_received: Instant,
}

impl<T> IrcTransport<T>
where
    T: AsyncRead + AsyncWrite,
{
    /// Creates a new `IrcTransport` from the given IRC stream.
    pub fn new(config: &Config, inner: Framed<T, IrcCodec>) -> IrcTransport<T> {
        let timer = tokio_timer::wheel().build();
        IrcTransport {
            inner: inner,
            burst_timer: tokio_timer::wheel().build(),
            rolling_burst_window: VecDeque::new(),
            burst_window_length: config.burst_window_length() as u64,
            max_burst_messages: config.max_messages_in_burst() as u64,
            current_burst_messages: 0,
            ping_timer: timer.interval(Duration::from_secs(config.ping_time() as u64)),
            ping_timeout: config.ping_timeout() as u64,
            last_ping_data: String::new(),
            last_ping_sent: Instant::now(),
            last_pong_received: Instant::now(),
        }
    }

    /// Gets the inner stream underlying the `IrcTransport`.
    pub fn into_inner(self) -> Framed<T, IrcCodec> {
        self.inner
    }

    fn ping_timed_out(&self) -> bool {
        if self.last_pong_received < self.last_ping_sent {
            self.last_ping_sent.elapsed().as_secs() >= self.ping_timeout
        } else {
            false
        }
    }

    fn send_ping(&mut self) -> error::Result<()> {
        self.last_ping_sent = Instant::now();
        self.last_ping_data = format!("{}", Local::now().timestamp());
        let data = self.last_ping_data.clone();
        let result = self.start_send(Command::PING(data, None).into())?;
        assert!(result.is_ready());
        self.poll_complete()?;
        Ok(())
    }

    fn rolling_burst_window_front(&mut self) -> Result<Async<()>, tokio_timer::TimerError> {
        self.rolling_burst_window.front_mut().map(|w| w.poll()).unwrap_or(Ok(Async::NotReady))
    }
}

impl<T> Stream for IrcTransport<T>
where
    T: AsyncRead + AsyncWrite,
{
    type Item = Message;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if self.ping_timed_out() {
            self.close()?;
            return Err(error::ErrorKind::PingTimeout.into())
        }

        let timer_poll = self.ping_timer.poll()?;
        let inner_poll = self.inner.poll()?;

        match (inner_poll, timer_poll) {
            (Async::NotReady, Async::NotReady) => Ok(Async::NotReady),
            (Async::NotReady, Async::Ready(msg)) => {
                assert!(msg.is_some());
                self.send_ping()?;
                Ok(Async::NotReady)
            }
            (Async::Ready(None), _) => Ok(Async::Ready(None)),
            (Async::Ready(Some(msg)), _) => {
                match timer_poll {
                    Async::Ready(msg) => {
                        assert!(msg.is_some());
                        self.send_ping()?;
                    }
                    Async::NotReady => (),
                }

                match msg.command {
                    // Automatically respond to PINGs from the server.
                    Command::PING(ref data, _) => {
                        let result = self.start_send(Command::PONG(data.to_owned(), None).into())?;
                        assert!(result.is_ready());
                        self.poll_complete()?;
                    }
                    // Check PONG responses from the server.
                    Command::PONG(ref data, None) |
                    Command::PONG(_, Some(ref data)) => {
                        if self.last_ping_data == &data[..] {
                            self.last_pong_received = Instant::now();
                        }
                    }
                    _ => (),
                }

                Ok(Async::Ready(Some(msg)))
            }
        }
    }
}

impl<T> Sink for IrcTransport<T>
where
    T: AsyncRead + AsyncWrite,
{
    type SinkItem = Message;
    type SinkError = error::Error;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        if self.ping_timed_out() {
            self.close()?;
            Err(error::ErrorKind::PingTimeout.into())
        } else {
            // Check if the oldest message in the rolling window is discounted.
            match self.rolling_burst_window_front()? {
                Async::NotReady => (),
                Async::Ready(()) => {
                    self.current_burst_messages -= 1;
                    self.rolling_burst_window.pop_front();
                }
            }

            // Throttling if too many messages have been sent recently.
            if self.current_burst_messages >= self.max_burst_messages {
                // When throttled, we know we need to finish sending what's already queued up.
                self.poll_complete()?;
                return Ok(AsyncSink::NotReady(item))
            }

            match self.inner.start_send(item)? {
                AsyncSink::NotReady(item) => Ok(AsyncSink::NotReady(item)),
                AsyncSink::Ready => {
                    self.current_burst_messages += 1;
                    self.rolling_burst_window.push_back(self.burst_timer.sleep(Duration::from_secs(
                        self.burst_window_length
                    )));
                    Ok(AsyncSink::Ready)
                }
            }
        }
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        if self.ping_timed_out() {
            self.close()?;
            Err(error::ErrorKind::PingTimeout.into())
        } else {
            Ok(self.inner.poll_complete()?)
        }
    }

    fn close(&mut self) -> Poll<(), Self::SinkError> {
        self.inner.close()
    }
}

/// A view of the logs from a particular `Logged` transport.
#[derive(Clone, Debug)]
pub struct LogView {
    sent: Arc<RwLock<Vec<Message>>>,
    received: Arc<RwLock<Vec<Message>>>,
}

impl LogView {
    /// Gets a read guard for all the messages sent on the transport.
    pub fn sent(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
        self.sent.read().map_err(
            |_| error::ErrorKind::PoisonedLog.into(),
        )
    }

    /// Gets a read guard for all the messages received on the transport.
    pub fn received(&self) -> error::Result<RwLockReadGuard<Vec<Message>>> {
        self.received.read().map_err(
            |_| error::ErrorKind::PoisonedLog.into(),
        )
    }
}

/// A logged version of the `IrcTransport` that records all sent and received messages.
/// Note: this will introduce some performance overhead by cloning all messages.
pub struct Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    inner: IrcTransport<T>,
    view: LogView,
}

impl<T> Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    /// Wraps the given `IrcTransport` in logging.
    pub fn wrap(inner: IrcTransport<T>) -> Logged<T> {
        Logged {
            inner: inner,
            view: LogView {
                sent: Arc::new(RwLock::new(vec![])),
                received: Arc::new(RwLock::new(vec![])),
            },
        }
    }

    /// Gets a view of the logging for this transport.
    pub fn view(&self) -> LogView {
        self.view.clone()
    }
}

impl<T> Stream for Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    type Item = Message;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match try_ready!(self.inner.poll()) {
            Some(msg) => {
                let recv: error::Result<_> = self.view.received.write().map_err(|_| {
                    error::ErrorKind::PoisonedLog.into()
                });
                recv?.push(msg.clone());
                Ok(Async::Ready(Some(msg)))
            }
            None => Ok(Async::Ready(None)),
        }
    }
}

impl<T> Sink for Logged<T>
where
    T: AsyncRead + AsyncWrite,
{
    type SinkItem = Message;
    type SinkError = error::Error;

    fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
        let res = self.inner.start_send(item.clone())?;
        let sent: error::Result<_> = self.view.sent.write().map_err(|_| {
            error::ErrorKind::PoisonedLog.into()
        });
        sent?.push(item);
        Ok(res)
    }

    fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
        Ok(self.inner.poll_complete()?)
    }
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;

use bytes::{Buf, Bytes};

use futures::future;
use futures::stream;
use futures::stream::Stream;
use futures::stream::StreamExt;

use crate::bytesx::bytes_extend;
use crate::error::*;
use crate::result;
use futures::task::Context;
use httpbis::DataOrTrailers;
use httpbis::HttpStreamAfterHeaders;
use std::pin::Pin;
use std::task::Poll;

fn read_u32_be(bytes: &[u8]) -> u32 {
    0 | ((bytes[0] as u32) << 24)
        | ((bytes[1] as u32) << 16)
        | ((bytes[2] as u32) << 8)
        | ((bytes[3] as u32) << 0)
}

fn write_u32_be(v: u32) -> [u8; 4] {
    [
        (v >> 24) as u8,
        (v >> 16) as u8,
        (v >> 8) as u8,
        (v >> 0) as u8,
    ]
}

pub const GRPC_HEADER_LEN: usize = 5;

/// Return frame len
pub fn parse_grpc_frame_0(stream: &[u8]) -> result::Result<Option<usize>> {
    let mut stream_copy = stream;
    let len = match parse_grpc_frame_header(&mut stream_copy)? {
        Some(len) => len,
        None => return Ok(None),
    };

    let end = len as usize + GRPC_HEADER_LEN;
    if end > stream.len() {
        return Ok(None);
    }

    Ok(Some(len as usize))
}

pub fn parse_grpc_frame_header<B: Buf>(stream: &mut B) -> result::Result<Option<u32>> {
    if stream.remaining() < GRPC_HEADER_LEN {
        return Ok(None);
    }

    let compressed = match stream.get_u8() {
        0 => false,
        1 => true,
        _ => return Err(Error::Other("unknown compression flag")),
    };
    if compressed {
        return Err(Error::Other("compression is not implemented"));
    }
    Ok(Some(stream.get_u32()))
}

// return message and size consumed
pub fn parse_grpc_frame(stream: &[u8]) -> result::Result<Option<(&[u8], usize)>> {
    parse_grpc_frame_0(stream).map(|o| {
        o.map(|len| {
            (
                &stream[GRPC_HEADER_LEN..len + GRPC_HEADER_LEN],
                len + GRPC_HEADER_LEN,
            )
        })
    })
}

pub fn parse_grpc_frame_from_bytes(stream: &mut Bytes) -> result::Result<Option<Bytes>> {
    if let Some(len) = parse_grpc_frame_0(&stream)? {
        let r = stream.slice(GRPC_HEADER_LEN..len + GRPC_HEADER_LEN);
        stream.advance(len + GRPC_HEADER_LEN);
        Ok(Some(r))
    } else {
        Ok(None)
    }
}

pub fn parse_grpc_frames_from_bytes(stream: &mut Bytes) -> result::Result<Vec<Bytes>> {
    let mut r = Vec::new();
    loop {
        match parse_grpc_frame_from_bytes(stream)? {
            Some(bytes) => {
                r.push(bytes);
            }
            None => {
                return Ok(r);
            }
        }
    }
}

pub fn parse_grpc_frames_completely(stream: &[u8]) -> result::Result<Vec<&[u8]>> {
    let mut r = Vec::new();
    let mut pos = 0;
    while pos < stream.len() {
        let frame_opt = parse_grpc_frame(&stream[pos..])?;
        match frame_opt {
            None => return Err(Error::Other("not complete frames")),
            Some((frame, len)) => {
                r.push(frame);
                pos += len;
            }
        }
    }
    Ok(r)
}

#[allow(dead_code)]
pub fn parse_grpc_frame_completely(stream: &[u8]) -> result::Result<&[u8]> {
    let frames = parse_grpc_frames_completely(stream)?;
    if frames.len() == 1 {
        Ok(frames[0])
    } else {
        Err(Error::Other("expecting exactly one frame"))
    }
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame_cb<F, E>(stream: &mut Vec<u8>, frame: F) -> Result<(), E>
where
    F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
{
    stream.push(0); // compressed flag
    let size_pos = stream.len();
    stream.extend_from_slice(&[0, 0, 0, 0]); // len
    let frame_start = stream.len();
    frame(stream)?;
    let frame_size = stream.len() - frame_start;
    assert!(frame_size <= u32::max_value() as usize);
    stream[size_pos..size_pos + 4].copy_from_slice(&write_u32_be(frame_size as u32));
    Ok(())
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame(stream: &mut Vec<u8>, frame: &[u8]) {
    assert!(frame.len() <= u32::max_value() as usize);
    stream.reserve(5 + frame.len());
    stream.push(0); // compressed flag
    stream.extend(&write_u32_be(frame.len() as u32));
    stream.extend(frame);
}

pub fn write_grpc_frame_to_vec(frame: &[u8]) -> Vec<u8> {
    let mut r = Vec::new();
    write_grpc_frame(&mut r, frame);
    r
}

trait RequestOrResponse {
    fn need_trailing_header() -> bool;
}

pub struct GrpcFrameFromHttpFramesStreamRequest {
    http_stream_stream: HttpStreamAfterHeaders,
    buf: Bytes,
    parsed_frames: VecDeque<Bytes>,
    error: Option<stream::Once<future::Ready<crate::Result<Bytes>>>>,
}

impl GrpcFrameFromHttpFramesStreamRequest {
    pub fn _new(http_stream_stream: HttpStreamAfterHeaders) -> Self {
        GrpcFrameFromHttpFramesStreamRequest {
            http_stream_stream,
            buf: Bytes::new(),
            parsed_frames: VecDeque::new(),
            error: None,
        }
    }
}

impl Stream for GrpcFrameFromHttpFramesStreamRequest {
    type Item = crate::Result<Bytes>;

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<Bytes>>> {
        loop {
            if let Some(ref mut error) = self.error {
                return error.poll_next_unpin(cx);
            }

            let p_r = parse_grpc_frames_from_bytes(&mut self.buf);

            self.parsed_frames.extend(match p_r {
                Ok(r) => r,
                Err(e) => {
                    self.error = Some(stream::once(future::ready(Err(e))));
                    continue;
                }
            });

            if let Some(frame) = self.parsed_frames.pop_front() {
                return Poll::Ready(Some(Ok(frame)));
            }

            let part_opt = match self.http_stream_stream.poll_next_unpin(cx)? {
                Poll::Pending => return Poll::Pending,
                Poll::Ready(part_opt) => part_opt,
            };
            let part = match part_opt {
                None => {
                    if self.buf.is_empty() {
                        return Poll::Ready(None);
                    } else {
                        self.error = Some(stream::once(future::err(Error::Other("partial frame"))));
                        continue;
                    }
                }
                Some(part) => part,
            };

            match part {
                // unexpected but OK
                DataOrTrailers::Trailers(..) => (),
                DataOrTrailers::Data(data, ..) => {
                    bytes_extend(&mut self.buf, data);
                }
            }
        }
    }
}

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

    #[test]
    fn test_parse_grpc_frame() {
        assert_eq!(None, parse_grpc_frame(b"").unwrap());
        assert_eq!(None, parse_grpc_frame(b"1").unwrap());
        assert_eq!(None, parse_grpc_frame(b"14sc").unwrap());
        assert_eq!(
            None,
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05wo").unwrap()
        );
        assert_eq!(
            Some((&b"\x0a\x05world"[..], 12)),
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05world").unwrap()
        );
    }

    #[test]
    fn test_parse_grpc_frames_from_bytes() {
        fn t(r: &[&[u8]], input: &[u8], trail: &[u8]) {
            let mut b = Bytes::new();
            bytes_extend_from_slice(&mut b, input);
            bytes_extend_from_slice(&mut b, trail);

            let r: Vec<Bytes> = r.into_iter().map(|&s| Bytes::copy_from_slice(s)).collect();

            let rr = parse_grpc_frames_from_bytes(&mut b).unwrap();
            assert_eq!(r, rr);
            assert_eq!(trail, b.as_ref());
        }

        t(&[], &[], &[]);
        t(&[], &[], &b"1"[..]);
        t(&[], &[], &b"14sc"[..]);
        t(
            &[&b"\x0a\x05world"[..]],
            &b"\x00\x00\x00\x00\x07\x0a\x05world"[..],
            &[],
        );
        t(
            &[&b"ab"[..], &b"cde"[..]],
            &b"\0\x00\x00\x00\x02ab\0\x00\x00\x00\x03cde"[..],
            &b"\x00"[..],
        );
    }

    #[test]
    fn test_write_grpc_frame_cb() {
        let mut f = Vec::new();
        write_grpc_frame_cb(&mut f, |f| Ok::<_, ()>(f.extend_from_slice(&[0x17, 0x19]))).unwrap();
        assert_eq!(&b"\x00\x00\x00\x00\x02\x17\x19"[..], f.as_slice());
    }
}
<commit_msg>Do not reallocate `Bytes` objects in GrpcFrameFromHttpFramesStreamRequest<commit_after>use std::collections::VecDeque;

use bytes::{Buf, Bytes};

use futures::future;
use futures::stream;
use futures::stream::Stream;
use futures::stream::StreamExt;

use crate::bytesx::bytes_extend;
use crate::error::*;
use crate::proto::grpc_frame_parser::GrpcFrameParser;
use crate::result;
use futures::task::Context;
use httpbis::DataOrTrailers;
use httpbis::HttpStreamAfterHeaders;
use std::pin::Pin;
use std::task::Poll;

fn read_u32_be(bytes: &[u8]) -> u32 {
    0 | ((bytes[0] as u32) << 24)
        | ((bytes[1] as u32) << 16)
        | ((bytes[2] as u32) << 8)
        | ((bytes[3] as u32) << 0)
}

fn write_u32_be(v: u32) -> [u8; 4] {
    [
        (v >> 24) as u8,
        (v >> 16) as u8,
        (v >> 8) as u8,
        (v >> 0) as u8,
    ]
}

pub const GRPC_HEADER_LEN: usize = 5;

/// Return frame len
pub fn parse_grpc_frame_0(stream: &[u8]) -> result::Result<Option<usize>> {
    let mut stream_copy = stream;
    let len = match parse_grpc_frame_header(&mut stream_copy)? {
        Some(len) => len,
        None => return Ok(None),
    };

    let end = len as usize + GRPC_HEADER_LEN;
    if end > stream.len() {
        return Ok(None);
    }

    Ok(Some(len as usize))
}

pub fn parse_grpc_frame_header<B: Buf>(stream: &mut B) -> result::Result<Option<u32>> {
    if stream.remaining() < GRPC_HEADER_LEN {
        return Ok(None);
    }

    let compressed = match stream.get_u8() {
        0 => false,
        1 => true,
        _ => return Err(Error::Other("unknown compression flag")),
    };
    if compressed {
        return Err(Error::Other("compression is not implemented"));
    }
    Ok(Some(stream.get_u32()))
}

// return message and size consumed
pub fn parse_grpc_frame(stream: &[u8]) -> result::Result<Option<(&[u8], usize)>> {
    parse_grpc_frame_0(stream).map(|o| {
        o.map(|len| {
            (
                &stream[GRPC_HEADER_LEN..len + GRPC_HEADER_LEN],
                len + GRPC_HEADER_LEN,
            )
        })
    })
}

pub fn parse_grpc_frame_from_bytes(stream: &mut Bytes) -> result::Result<Option<Bytes>> {
    if let Some(len) = parse_grpc_frame_0(&stream)? {
        let r = stream.slice(GRPC_HEADER_LEN..len + GRPC_HEADER_LEN);
        stream.advance(len + GRPC_HEADER_LEN);
        Ok(Some(r))
    } else {
        Ok(None)
    }
}

pub fn parse_grpc_frames_from_bytes(stream: &mut Bytes) -> result::Result<Vec<Bytes>> {
    let mut r = Vec::new();
    loop {
        match parse_grpc_frame_from_bytes(stream)? {
            Some(bytes) => {
                r.push(bytes);
            }
            None => {
                return Ok(r);
            }
        }
    }
}

pub fn parse_grpc_frames_completely(stream: &[u8]) -> result::Result<Vec<&[u8]>> {
    let mut r = Vec::new();
    let mut pos = 0;
    while pos < stream.len() {
        let frame_opt = parse_grpc_frame(&stream[pos..])?;
        match frame_opt {
            None => return Err(Error::Other("not complete frames")),
            Some((frame, len)) => {
                r.push(frame);
                pos += len;
            }
        }
    }
    Ok(r)
}

#[allow(dead_code)]
pub fn parse_grpc_frame_completely(stream: &[u8]) -> result::Result<&[u8]> {
    let frames = parse_grpc_frames_completely(stream)?;
    if frames.len() == 1 {
        Ok(frames[0])
    } else {
        Err(Error::Other("expecting exactly one frame"))
    }
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame_cb<F, E>(stream: &mut Vec<u8>, frame: F) -> Result<(), E>
where
    F: FnOnce(&mut Vec<u8>) -> Result<(), E>,
{
    stream.push(0); // compressed flag
    let size_pos = stream.len();
    stream.extend_from_slice(&[0, 0, 0, 0]); // len
    let frame_start = stream.len();
    frame(stream)?;
    let frame_size = stream.len() - frame_start;
    assert!(frame_size <= u32::max_value() as usize);
    stream[size_pos..size_pos + 4].copy_from_slice(&write_u32_be(frame_size as u32));
    Ok(())
}

/// Encode data into grpc frame (add frame prefix)
pub fn write_grpc_frame(stream: &mut Vec<u8>, frame: &[u8]) {
    assert!(frame.len() <= u32::max_value() as usize);
    stream.reserve(5 + frame.len());
    stream.push(0); // compressed flag
    stream.extend(&write_u32_be(frame.len() as u32));
    stream.extend(frame);
}

pub fn write_grpc_frame_to_vec(frame: &[u8]) -> Vec<u8> {
    let mut r = Vec::new();
    write_grpc_frame(&mut r, frame);
    r
}

trait RequestOrResponse {
    fn need_trailing_header() -> bool;
}

pub struct GrpcFrameFromHttpFramesStreamRequest {
    http_stream_stream: HttpStreamAfterHeaders,
    buf: GrpcFrameParser,
    parsed_frames: VecDeque<Bytes>,
    error: Option<stream::Once<future::Ready<crate::Result<Bytes>>>>,
}

impl GrpcFrameFromHttpFramesStreamRequest {
    pub fn _new(http_stream_stream: HttpStreamAfterHeaders) -> Self {
        GrpcFrameFromHttpFramesStreamRequest {
            http_stream_stream,
            buf: GrpcFrameParser::default(),
            parsed_frames: VecDeque::new(),
            error: None,
        }
    }
}

impl Stream for GrpcFrameFromHttpFramesStreamRequest {
    type Item = crate::Result<Bytes>;

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<crate::Result<Bytes>>> {
        loop {
            if let Some(ref mut error) = self.error {
                return error.poll_next_unpin(cx);
            }

            let p_r = self.buf.next_frames();

            self.parsed_frames.extend(match p_r {
                Ok(r) => r,
                Err(e) => {
                    self.error = Some(stream::once(future::ready(Err(e))));
                    continue;
                }
            });

            if let Some(frame) = self.parsed_frames.pop_front() {
                return Poll::Ready(Some(Ok(frame)));
            }

            let part_opt = match self.http_stream_stream.poll_next_unpin(cx)? {
                Poll::Pending => return Poll::Pending,
                Poll::Ready(part_opt) => part_opt,
            };
            let part = match part_opt {
                None => {
                    if self.buf.is_empty() {
                        return Poll::Ready(None);
                    } else {
                        self.error = Some(stream::once(future::err(Error::Other("partial frame"))));
                        continue;
                    }
                }
                Some(part) => part,
            };

            match part {
                // unexpected but OK
                DataOrTrailers::Trailers(..) => (),
                DataOrTrailers::Data(data, ..) => {
                    self.buf.enqueue(data);
                }
            }
        }
    }
}

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

    #[test]
    fn test_parse_grpc_frame() {
        assert_eq!(None, parse_grpc_frame(b"").unwrap());
        assert_eq!(None, parse_grpc_frame(b"1").unwrap());
        assert_eq!(None, parse_grpc_frame(b"14sc").unwrap());
        assert_eq!(
            None,
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05wo").unwrap()
        );
        assert_eq!(
            Some((&b"\x0a\x05world"[..], 12)),
            parse_grpc_frame(b"\x00\x00\x00\x00\x07\x0a\x05world").unwrap()
        );
    }

    #[test]
    fn test_parse_grpc_frames_from_bytes() {
        fn t(r: &[&[u8]], input: &[u8], trail: &[u8]) {
            let mut b = Bytes::new();
            bytes_extend_from_slice(&mut b, input);
            bytes_extend_from_slice(&mut b, trail);

            let r: Vec<Bytes> = r.into_iter().map(|&s| Bytes::copy_from_slice(s)).collect();

            let rr = parse_grpc_frames_from_bytes(&mut b).unwrap();
            assert_eq!(r, rr);
            assert_eq!(trail, b.as_ref());
        }

        t(&[], &[], &[]);
        t(&[], &[], &b"1"[..]);
        t(&[], &[], &b"14sc"[..]);
        t(
            &[&b"\x0a\x05world"[..]],
            &b"\x00\x00\x00\x00\x07\x0a\x05world"[..],
            &[],
        );
        t(
            &[&b"ab"[..], &b"cde"[..]],
            &b"\0\x00\x00\x00\x02ab\0\x00\x00\x00\x03cde"[..],
            &b"\x00"[..],
        );
    }

    #[test]
    fn test_write_grpc_frame_cb() {
        let mut f = Vec::new();
        write_grpc_frame_cb(&mut f, |f| Ok::<_, ()>(f.extend_from_slice(&[0x17, 0x19]))).unwrap();
        assert_eq!(&b"\x00\x00\x00\x00\x02\x17\x19"[..], f.as_slice());
    }
}
<|endoftext|>


<commit_before>// This file was generated by gir (dc20488) from gir-files (11e0e6d)
// DO NOT EDIT

use ApplicationInhibitFlags;
use Window;
use ffi;
use glib;
use glib::object::IsA;
use glib::translate::*;

glib_wrapper! {
    pub struct Application(Object<ffi::GtkApplication>);

    match fn {
        get_type => || ffi::gtk_application_get_type(),
    }
}

impl Application {
    pub fn new(application_id: Option<&str>, flags: glib::ApplicationFlags) -> Application {
        assert_initialized_main_thread!();
        unsafe {
            from_glib_full(ffi::gtk_application_new(application_id.to_glib_none().0, flags))
        }
    }

    //pub fn add_accelerator(&self, accelerator: &str, action_name: &str, parameter: /*Ignored*/Option<&glib::Variant>) {
    //    unsafe { TODO: call ffi::gtk_application_add_accelerator() }
    //}

    pub fn add_window<T: IsA<Window>>(&self, window: &T) {
        unsafe {
            ffi::gtk_application_add_window(self.to_glib_none().0, window.to_glib_none().0);
        }
    }

    #[cfg(feature = "3.12")]
    pub fn get_accels_for_action(&self, detailed_action_name: &str) -> Vec<String> {
        unsafe {
            FromGlibPtrContainer::from_glib_full(ffi::gtk_application_get_accels_for_action(self.to_glib_none().0, detailed_action_name.to_glib_none().0))
        }
    }

    #[cfg(feature = "3.14")]
    pub fn get_actions_for_accel(&self, accel: &str) -> Vec<String> {
        unsafe {
            FromGlibPtrContainer::from_glib_full(ffi::gtk_application_get_actions_for_accel(self.to_glib_none().0, accel.to_glib_none().0))
        }
    }

    #[cfg(feature = "3.6")]
    pub fn get_active_window(&self) -> Option<Window> {
        unsafe {
            from_glib_none(ffi::gtk_application_get_active_window(self.to_glib_none().0))
        }
    }

    //pub fn get_app_menu(&self) -> /*Ignored*/Option<glib::MenuModel> {
    //    unsafe { TODO: call ffi::gtk_application_get_app_menu() }
    //}

    //#[cfg(feature = "3.14")]
    //pub fn get_menu_by_id(&self, id: &str) -> /*Ignored*/Option<glib::Menu> {
    //    unsafe { TODO: call ffi::gtk_application_get_menu_by_id() }
    //}

    //pub fn get_menubar(&self) -> /*Ignored*/Option<glib::MenuModel> {
    //    unsafe { TODO: call ffi::gtk_application_get_menubar() }
    //}

    #[cfg(feature = "3.6")]
    pub fn get_window_by_id(&self, id: u32) -> Option<Window> {
        unsafe {
            from_glib_none(ffi::gtk_application_get_window_by_id(self.to_glib_none().0, id))
        }
    }

    pub fn get_windows(&self) -> Vec<Window> {
        unsafe {
            FromGlibPtrContainer::from_glib_none(ffi::gtk_application_get_windows(self.to_glib_none().0))
        }
    }

    pub fn inhibit<T: IsA<Window>>(&self, window: Option<&T>, flags: ApplicationInhibitFlags, reason: Option<&str>) -> u32 {
        unsafe {
            ffi::gtk_application_inhibit(self.to_glib_none().0, window.to_glib_none().0, flags, reason.to_glib_none().0)
        }
    }

    pub fn is_inhibited(&self, flags: ApplicationInhibitFlags) -> bool {
        unsafe {
            from_glib(ffi::gtk_application_is_inhibited(self.to_glib_none().0, flags))
        }
    }

    #[cfg(feature = "3.12")]
    pub fn list_action_descriptions(&self) -> Vec<String> {
        unsafe {
            FromGlibPtrContainer::from_glib_full(ffi::gtk_application_list_action_descriptions(self.to_glib_none().0))
        }
    }

    #[cfg(feature = "3.14")]
    pub fn prefers_app_menu(&self) -> bool {
        unsafe {
            from_glib(ffi::gtk_application_prefers_app_menu(self.to_glib_none().0))
        }
    }

    //pub fn remove_accelerator(&self, action_name: &str, parameter: /*Ignored*/Option<&glib::Variant>) {
    //    unsafe { TODO: call ffi::gtk_application_remove_accelerator() }
    //}

    pub fn remove_window<T: IsA<Window>>(&self, window: &T) {
        unsafe {
            ffi::gtk_application_remove_window(self.to_glib_none().0, window.to_glib_none().0);
        }
    }

    #[cfg(feature = "3.12")]
    pub fn set_accels_for_action(&self, detailed_action_name: &str, accels: &[&str]) {
        unsafe {
            ffi::gtk_application_set_accels_for_action(self.to_glib_none().0, detailed_action_name.to_glib_none().0, accels.to_glib_none().0);
        }
    }

    //pub fn set_app_menu<T: IsA</*Ignored*/glib::MenuModel>>(&self, app_menu: Option<&T>) {
    //    unsafe { TODO: call ffi::gtk_application_set_app_menu() }
    //}

    //pub fn set_menubar<T: IsA</*Ignored*/glib::MenuModel>>(&self, menubar: Option<&T>) {
    //    unsafe { TODO: call ffi::gtk_application_set_menubar() }
    //}

    pub fn uninhibit(&self, cookie: u32) {
        unsafe {
            ffi::gtk_application_uninhibit(self.to_glib_none().0, cookie);
        }
    }
}
<commit_msg>Regen<commit_after>// This file was generated by gir (dc20488) from gir-files (11e0e6d)
// DO NOT EDIT

use ApplicationInhibitFlags;
use Window;
use ffi;
use glib::object::IsA;
use glib::translate::*;

glib_wrapper! {
    pub struct Application(Object<ffi::GtkApplication>);

    match fn {
        get_type => || ffi::gtk_application_get_type(),
    }
}

impl Application {
    //pub fn add_accelerator(&self, accelerator: &str, action_name: &str, parameter: /*Ignored*/Option<&glib::Variant>) {
    //    unsafe { TODO: call ffi::gtk_application_add_accelerator() }
    //}

    pub fn add_window<T: IsA<Window>>(&self, window: &T) {
        unsafe {
            ffi::gtk_application_add_window(self.to_glib_none().0, window.to_glib_none().0);
        }
    }

    #[cfg(feature = "3.12")]
    pub fn get_accels_for_action(&self, detailed_action_name: &str) -> Vec<String> {
        unsafe {
            FromGlibPtrContainer::from_glib_full(ffi::gtk_application_get_accels_for_action(self.to_glib_none().0, detailed_action_name.to_glib_none().0))
        }
    }

    #[cfg(feature = "3.14")]
    pub fn get_actions_for_accel(&self, accel: &str) -> Vec<String> {
        unsafe {
            FromGlibPtrContainer::from_glib_full(ffi::gtk_application_get_actions_for_accel(self.to_glib_none().0, accel.to_glib_none().0))
        }
    }

    #[cfg(feature = "3.6")]
    pub fn get_active_window(&self) -> Option<Window> {
        unsafe {
            from_glib_none(ffi::gtk_application_get_active_window(self.to_glib_none().0))
        }
    }

    //pub fn get_app_menu(&self) -> /*Ignored*/Option<glib::MenuModel> {
    //    unsafe { TODO: call ffi::gtk_application_get_app_menu() }
    //}

    //#[cfg(feature = "3.14")]
    //pub fn get_menu_by_id(&self, id: &str) -> /*Ignored*/Option<glib::Menu> {
    //    unsafe { TODO: call ffi::gtk_application_get_menu_by_id() }
    //}

    //pub fn get_menubar(&self) -> /*Ignored*/Option<glib::MenuModel> {
    //    unsafe { TODO: call ffi::gtk_application_get_menubar() }
    //}

    #[cfg(feature = "3.6")]
    pub fn get_window_by_id(&self, id: u32) -> Option<Window> {
        unsafe {
            from_glib_none(ffi::gtk_application_get_window_by_id(self.to_glib_none().0, id))
        }
    }

    pub fn get_windows(&self) -> Vec<Window> {
        unsafe {
            FromGlibPtrContainer::from_glib_none(ffi::gtk_application_get_windows(self.to_glib_none().0))
        }
    }

    pub fn inhibit<T: IsA<Window>>(&self, window: Option<&T>, flags: ApplicationInhibitFlags, reason: Option<&str>) -> u32 {
        unsafe {
            ffi::gtk_application_inhibit(self.to_glib_none().0, window.to_glib_none().0, flags, reason.to_glib_none().0)
        }
    }

    pub fn is_inhibited(&self, flags: ApplicationInhibitFlags) -> bool {
        unsafe {
            from_glib(ffi::gtk_application_is_inhibited(self.to_glib_none().0, flags))
        }
    }

    #[cfg(feature = "3.12")]
    pub fn list_action_descriptions(&self) -> Vec<String> {
        unsafe {
            FromGlibPtrContainer::from_glib_full(ffi::gtk_application_list_action_descriptions(self.to_glib_none().0))
        }
    }

    #[cfg(feature = "3.14")]
    pub fn prefers_app_menu(&self) -> bool {
        unsafe {
            from_glib(ffi::gtk_application_prefers_app_menu(self.to_glib_none().0))
        }
    }

    //pub fn remove_accelerator(&self, action_name: &str, parameter: /*Ignored*/Option<&glib::Variant>) {
    //    unsafe { TODO: call ffi::gtk_application_remove_accelerator() }
    //}

    pub fn remove_window<T: IsA<Window>>(&self, window: &T) {
        unsafe {
            ffi::gtk_application_remove_window(self.to_glib_none().0, window.to_glib_none().0);
        }
    }

    #[cfg(feature = "3.12")]
    pub fn set_accels_for_action(&self, detailed_action_name: &str, accels: &[&str]) {
        unsafe {
            ffi::gtk_application_set_accels_for_action(self.to_glib_none().0, detailed_action_name.to_glib_none().0, accels.to_glib_none().0);
        }
    }

    //pub fn set_app_menu<T: IsA</*Ignored*/glib::MenuModel>>(&self, app_menu: Option<&T>) {
    //    unsafe { TODO: call ffi::gtk_application_set_app_menu() }
    //}

    //pub fn set_menubar<T: IsA</*Ignored*/glib::MenuModel>>(&self, menubar: Option<&T>) {
    //    unsafe { TODO: call ffi::gtk_application_set_menubar() }
    //}

    pub fn uninhibit(&self, cookie: u32) {
        unsafe {
            ffi::gtk_application_uninhibit(self.to_glib_none().0, cookie);
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::hash_map::HashMap;
use std::mem::size_of;

use baseline::fct::{JitBaselineFct, JitFctId, JitFct};
use cpu::{Mem, FREG_PARAMS, REG_FP, REG_PARAMS, REG_RESULT, REG_SP, REG_THREAD};
use ctxt::{exception_get_and_clear, get_ctxt, FctId, SemContext};
use masm::MacroAssembler;
use mem;
use os::signal::Trap;
use exception::DoraToNativeInfo;
use ty::{BuiltinType, MachineMode};

pub struct NativeFcts {
    map: HashMap<*const u8, JitFctId>,
}

impl NativeFcts {
    pub fn new() -> NativeFcts {
        NativeFcts { map: HashMap::new() }
    }

    pub fn find_fct(&self, ptr: *const u8) -> Option<JitFctId> {
        self.map.get(&ptr).map(|&jit_fct_id| jit_fct_id)
    }

    pub fn insert_fct(&mut self, ptr: *const u8, fct: JitFctId) {
        self.map.entry(ptr).or_insert(fct);
    }
}

pub struct InternalFct<'a> {
    pub ptr: *const u8,
    pub args: &'a [BuiltinType],
    pub return_type: BuiltinType,
    pub throws: bool,
    pub id: FctId,
}

pub fn generate<'a, 'ast: 'a>(ctxt: &'a SemContext<'ast>, fct: InternalFct, dbg: bool) -> JitFctId {
    let ngen = NativeGen {
        ctxt: ctxt,
        masm: MacroAssembler::new(),
        fct: fct,
        dbg: dbg,
    };

    let jit_fct = ngen.generate();

    let jit_fct_id = ctxt.jit_fcts.len().into();
    ctxt.jit_fcts.push(JitFct::Base(jit_fct));

    jit_fct_id
}

struct NativeGen<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    masm: MacroAssembler,

    fct: InternalFct<'a>,
    dbg: bool,
}

impl<'a, 'ast> NativeGen<'a, 'ast>
where
    'ast: 'a,
{
    pub fn generate(mut self) -> JitBaselineFct {
        let save_return = self.fct.return_type != BuiltinType::Unit;
        let args = self.fct.args.len();

        let framesize =
            size_of::<DoraToNativeInfo>() as i32 +          // save d2n structure on stack
            mem::ptr_width() +                              // store thread register
            (args as i32 * mem::ptr_width()) +              // store arguments on stack
            if save_return { mem::ptr_width() } else { 0 }; // save return value on stack

        let framesize = mem::align_i32(framesize, 16);

        let offset_return = 0;
        let offset_args = offset_return + if save_return { mem::ptr_width() } else { 0 };
        let offset_thread = offset_args + mem::ptr_width();

        // `start_native_call` assumes that offset_dtn is on top of current stack frame.

        if self.dbg {
            self.masm.debug();
        }

        self.masm.prolog(framesize);

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_thread),
            REG_THREAD.into(),
        );

        save_params(&mut self.masm, self.fct.args, offset_args);

        self.masm.copy_reg(MachineMode::Ptr, REG_PARAMS[0], REG_FP);
        self.masm.copy_pc(REG_PARAMS[1]);
        self.masm.direct_call_without_info(
            start_native_call as *const u8,
        );

        restore_params(&mut self.masm, self.fct.args, offset_args);

        self.masm.direct_call_without_info(self.fct.ptr);

        if save_return {
            self.masm.store_mem(
                MachineMode::Ptr,
                Mem::Base(REG_SP, offset_return),
                REG_RESULT.into(),
            );
        }

        self.masm.direct_call_without_info(
            finish_native_call as *const u8,
        );

        let lbl_exception = self.masm.test_if_not_nil(REG_RESULT);

        if save_return {
            self.masm.load_mem(
                MachineMode::Ptr,
                REG_RESULT.into(),
                Mem::Base(REG_SP, offset_return),
            );
        }

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_THREAD.into(),
            Mem::Base(REG_SP, offset_thread),
        );

        self.masm.epilog(framesize, self.ctxt.polling_page.addr());

        self.masm.bind_label(lbl_exception);
        self.masm.trap(Trap::THROW);

        self.masm.jit(
            self.ctxt,
            framesize,
            self.fct.id,
            self.fct.throws,
        )
    }
}

fn save_params(masm: &mut MacroAssembler, args: &[BuiltinType], offset_args: i32) {
    let mut reg_idx = 0;
    let mut freg_idx = 0;
    let mut idx = 0;

    for &ty in args {
        let mode = ty.mode();
        let is_float = mode.is_float();
        let offset = offset_args + idx as i32 * 8;

        if is_float && freg_idx < FREG_PARAMS.len() {
            let freg = FREG_PARAMS[freg_idx].into();
            masm.store_mem(mode, Mem::Base(REG_SP, offset), freg);
            freg_idx += 1;
        } else if !is_float && reg_idx < REG_PARAMS.len() {
            let reg = REG_PARAMS[reg_idx].into();
            masm.store_mem(mode, Mem::Base(REG_SP, offset), reg);
            reg_idx += 1;
        } else {
            panic!("parameter saved on stack");
        }

        idx += 1;
    }
}

fn restore_params(masm: &mut MacroAssembler, args: &[BuiltinType], offset_args: i32) {
    let mut reg_idx = 0;
    let mut freg_idx = 0;
    let mut idx = 0;

    for &ty in args {
        let mode = ty.mode();
        let is_float = mode.is_float();
        let offset = offset_args + idx as i32 * 8;

        if is_float && freg_idx < FREG_PARAMS.len() {
            let freg = FREG_PARAMS[freg_idx].into();
            masm.load_mem(mode, freg, Mem::Base(REG_SP, offset));
            freg_idx += 1;
        } else if !is_float && reg_idx < REG_PARAMS.len() {
            let reg = REG_PARAMS[reg_idx].into();
            masm.load_mem(mode, reg, Mem::Base(REG_SP, offset));
            reg_idx += 1;
        } else {
            panic!("parameter saved on stack");
        }

        idx += 1;
    }
}

fn start_native_call(fp: *const u8, pc: usize) {
    unsafe {
        // fp is framepointer of native stub

        let dtn_size = size_of::<DoraToNativeInfo>() as isize;
        let dtn: *mut DoraToNativeInfo = fp.offset(-dtn_size) as *mut DoraToNativeInfo;
        let dtn: &mut DoraToNativeInfo = &mut *dtn;

        dtn.fp = fp as usize;
        dtn.pc = pc;

        let ctxt = get_ctxt();

        ctxt.push_dtn(dtn);
        ctxt.handles.push_border();
    }
}

fn finish_native_call() -> *const u8 {
    let ctxt = get_ctxt();

    ctxt.handles.pop_border();
    ctxt.pop_dtn();

    exception_get_and_clear()
}
<commit_msg>fix: used wrong offset for storing thread register<commit_after>use std::collections::hash_map::HashMap;
use std::mem::size_of;

use baseline::fct::{JitBaselineFct, JitFctId, JitFct};
use cpu::{Mem, FREG_PARAMS, REG_FP, REG_PARAMS, REG_RESULT, REG_SP, REG_THREAD};
use ctxt::{exception_get_and_clear, get_ctxt, FctId, SemContext};
use masm::MacroAssembler;
use mem;
use os::signal::Trap;
use exception::DoraToNativeInfo;
use ty::{BuiltinType, MachineMode};

pub struct NativeFcts {
    map: HashMap<*const u8, JitFctId>,
}

impl NativeFcts {
    pub fn new() -> NativeFcts {
        NativeFcts { map: HashMap::new() }
    }

    pub fn find_fct(&self, ptr: *const u8) -> Option<JitFctId> {
        self.map.get(&ptr).map(|&jit_fct_id| jit_fct_id)
    }

    pub fn insert_fct(&mut self, ptr: *const u8, fct: JitFctId) {
        self.map.entry(ptr).or_insert(fct);
    }
}

pub struct InternalFct<'a> {
    pub ptr: *const u8,
    pub args: &'a [BuiltinType],
    pub return_type: BuiltinType,
    pub throws: bool,
    pub id: FctId,
}

pub fn generate<'a, 'ast: 'a>(ctxt: &'a SemContext<'ast>, fct: InternalFct, dbg: bool) -> JitFctId {
    let ngen = NativeGen {
        ctxt: ctxt,
        masm: MacroAssembler::new(),
        fct: fct,
        dbg: dbg,
    };

    let jit_fct = ngen.generate();

    let jit_fct_id = ctxt.jit_fcts.len().into();
    ctxt.jit_fcts.push(JitFct::Base(jit_fct));

    jit_fct_id
}

struct NativeGen<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    masm: MacroAssembler,

    fct: InternalFct<'a>,
    dbg: bool,
}

impl<'a, 'ast> NativeGen<'a, 'ast>
where
    'ast: 'a,
{
    pub fn generate(mut self) -> JitBaselineFct {
        let save_return = self.fct.return_type != BuiltinType::Unit;
        let args = self.fct.args.len();

        let framesize =
            size_of::<DoraToNativeInfo>() as i32 +          // save d2n structure on stack
            mem::ptr_width() +                              // store thread register
            (args as i32 * mem::ptr_width()) +              // store arguments on stack
            if save_return { mem::ptr_width() } else { 0 }; // save return value on stack

        let framesize = mem::align_i32(framesize, 16);

        let offset_return = 0;
        let offset_args = offset_return + if save_return { mem::ptr_width() } else { 0 };
        let offset_thread = offset_args + args as i32 * mem::ptr_width();

        // `start_native_call` assumes that offset_dtn is on top of current stack frame.

        if self.dbg {
            self.masm.debug();
        }

        self.masm.prolog(framesize);

        self.masm.store_mem(
            MachineMode::Ptr,
            Mem::Base(REG_SP, offset_thread),
            REG_THREAD.into(),
        );

        save_params(&mut self.masm, self.fct.args, offset_args);

        self.masm.copy_reg(MachineMode::Ptr, REG_PARAMS[0], REG_FP);
        self.masm.copy_pc(REG_PARAMS[1]);
        self.masm.direct_call_without_info(
            start_native_call as *const u8,
        );

        restore_params(&mut self.masm, self.fct.args, offset_args);

        self.masm.direct_call_without_info(self.fct.ptr);

        if save_return {
            self.masm.store_mem(
                MachineMode::Ptr,
                Mem::Base(REG_SP, offset_return),
                REG_RESULT.into(),
            );
        }

        self.masm.direct_call_without_info(
            finish_native_call as *const u8,
        );

        let lbl_exception = self.masm.test_if_not_nil(REG_RESULT);

        if save_return {
            self.masm.load_mem(
                MachineMode::Ptr,
                REG_RESULT.into(),
                Mem::Base(REG_SP, offset_return),
            );
        }

        self.masm.load_mem(
            MachineMode::Ptr,
            REG_THREAD.into(),
            Mem::Base(REG_SP, offset_thread),
        );

        self.masm.epilog(framesize, self.ctxt.polling_page.addr());

        self.masm.bind_label(lbl_exception);
        self.masm.trap(Trap::THROW);

        self.masm.jit(
            self.ctxt,
            framesize,
            self.fct.id,
            self.fct.throws,
        )
    }
}

fn save_params(masm: &mut MacroAssembler, args: &[BuiltinType], offset_args: i32) {
    let mut reg_idx = 0;
    let mut freg_idx = 0;
    let mut idx = 0;

    for &ty in args {
        let mode = ty.mode();
        let is_float = mode.is_float();
        let offset = offset_args + idx as i32 * 8;

        if is_float && freg_idx < FREG_PARAMS.len() {
            let freg = FREG_PARAMS[freg_idx].into();
            masm.store_mem(mode, Mem::Base(REG_SP, offset), freg);
            freg_idx += 1;
        } else if !is_float && reg_idx < REG_PARAMS.len() {
            let reg = REG_PARAMS[reg_idx].into();
            masm.store_mem(mode, Mem::Base(REG_SP, offset), reg);
            reg_idx += 1;
        } else {
            panic!("parameter saved on stack");
        }

        idx += 1;
    }
}

fn restore_params(masm: &mut MacroAssembler, args: &[BuiltinType], offset_args: i32) {
    let mut reg_idx = 0;
    let mut freg_idx = 0;
    let mut idx = 0;

    for &ty in args {
        let mode = ty.mode();
        let is_float = mode.is_float();
        let offset = offset_args + idx as i32 * 8;

        if is_float && freg_idx < FREG_PARAMS.len() {
            let freg = FREG_PARAMS[freg_idx].into();
            masm.load_mem(mode, freg, Mem::Base(REG_SP, offset));
            freg_idx += 1;
        } else if !is_float && reg_idx < REG_PARAMS.len() {
            let reg = REG_PARAMS[reg_idx].into();
            masm.load_mem(mode, reg, Mem::Base(REG_SP, offset));
            reg_idx += 1;
        } else {
            panic!("parameter saved on stack");
        }

        idx += 1;
    }
}

fn start_native_call(fp: *const u8, pc: usize) {
    unsafe {
        // fp is framepointer of native stub

        let dtn_size = size_of::<DoraToNativeInfo>() as isize;
        let dtn: *mut DoraToNativeInfo = fp.offset(-dtn_size) as *mut DoraToNativeInfo;
        let dtn: &mut DoraToNativeInfo = &mut *dtn;

        dtn.fp = fp as usize;
        dtn.pc = pc;

        let ctxt = get_ctxt();

        ctxt.push_dtn(dtn);
        ctxt.handles.push_border();
    }
}

fn finish_native_call() -> *const u8 {
    let ctxt = get_ctxt();

    ctxt.handles.pop_border();
    ctxt.pop_dtn();

    exception_get_and_clear()
}
<|endoftext|>