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

use admission_control_service::runtime::AdmissionControlRuntime;
use consensus::consensus_provider::{make_consensus_provider, ConsensusProvider};
use debug_interface::{node_debug_service::NodeDebugService, proto::create_node_debug_interface};
use executor::Executor;
use grpc_helpers::ServerHandle;
use grpcio::EnvBuilder;
use libra_config::config::{NetworkConfig, NodeConfig, RoleType};
use libra_logger::prelude::*;
use libra_mempool::MempoolRuntime;
use libra_metrics::metric_server;
use network::{
    validator_network::{
        network_builder::{NetworkBuilder, TransportType},
        LibraNetworkProvider,
        // when you add a new protocol const, you must add this in either
        // .direct_send_protocols or .rpc_protocols vector of network_builder in setup_network()
        ADMISSION_CONTROL_RPC_PROTOCOL,
        CONSENSUS_DIRECT_SEND_PROTOCOL,
        CONSENSUS_RPC_PROTOCOL,
        MEMPOOL_DIRECT_SEND_PROTOCOL,
        STATE_SYNCHRONIZER_DIRECT_SEND_PROTOCOL,
    },
    ProtocolId,
};
use state_synchronizer::StateSynchronizer;
use std::collections::HashMap;
use std::{sync::Arc, thread, time::Instant};
use storage_client::{StorageRead, StorageReadServiceClient, StorageWriteServiceClient};
use storage_service::start_storage_service;
use tokio::runtime::{Builder, Runtime};
use vm_runtime::LibraVM;

pub struct LibraHandle {
    _ac: AdmissionControlRuntime,
    _mempool: Option<MempoolRuntime>,
    _state_synchronizer: StateSynchronizer,
    _network_runtimes: Vec<Runtime>,
    consensus: Option<Box<dyn ConsensusProvider>>,
    _storage: ServerHandle,
    _debug: ServerHandle,
}

impl Drop for LibraHandle {
    fn drop(&mut self) {
        if let Some(consensus) = &mut self.consensus {
            consensus.stop();
        }
    }
}

fn setup_executor(config: &NodeConfig) -> Arc<Executor<LibraVM>> {
    let client_env = Arc::new(EnvBuilder::new().name_prefix("grpc-exe-sto-").build());
    let storage_read_client = Arc::new(StorageReadServiceClient::new(
        Arc::clone(&client_env),
        &config.storage.address,
        config.storage.port,
    ));
    let storage_write_client = Arc::new(StorageWriteServiceClient::new(
        Arc::clone(&client_env),
        &config.storage.address,
        config.storage.port,
        config.storage.grpc_max_receive_len,
    ));

    Arc::new(Executor::new(
        Arc::clone(&storage_read_client) as Arc<dyn StorageRead>,
        storage_write_client,
        config,
    ))
}

fn setup_debug_interface(config: &NodeConfig) -> ::grpcio::Server {
    let env = Arc::new(EnvBuilder::new().name_prefix("grpc-debug-").build());
    // Start Debug interface
    let debug_service = create_node_debug_interface(NodeDebugService::new());
    ::grpcio::ServerBuilder::new(env)
        .register_service(debug_service)
        .bind(
            config.debug_interface.address.clone(),
            config.debug_interface.admission_control_node_debug_port,
        )
        .build()
        .expect("Unable to create grpc server")
}

// TODO(abhayb): Move to network crate (similar to consensus).
pub fn setup_network(
    config: &mut NetworkConfig,
    role: RoleType,
) -> (Runtime, Box<dyn LibraNetworkProvider>) {
    let runtime = Builder::new()
        .thread_name("network-")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("Failed to start runtime. Won't be able to start networking.");
    let mut network_builder = NetworkBuilder::new(
        runtime.handle().clone(),
        config.peer_id,
        config.listen_address.clone(),
        role,
    );
    network_builder
        .permissioned(config.is_permissioned)
        .advertised_address(config.advertised_address.clone())
        .direct_send_protocols(vec![
            ProtocolId::from_static(CONSENSUS_DIRECT_SEND_PROTOCOL),
            ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL),
            ProtocolId::from_static(STATE_SYNCHRONIZER_DIRECT_SEND_PROTOCOL),
        ])
        .rpc_protocols(vec![
            ProtocolId::from_static(CONSENSUS_RPC_PROTOCOL),
            ProtocolId::from_static(ADMISSION_CONTROL_RPC_PROTOCOL),
        ]);
    if config.is_permissioned {
        // If the node wants to run in permissioned mode, it should also have authentication and
        // encryption.
        assert!(
            config.enable_encryption_and_authentication,
            "Permissioned network end-points should use authentication"
        );
        let seed_peers = config.seed_peers.seed_peers.clone();
        let signing_private = config
            .network_keypairs
            .signing_keys
            .take_private()
            .expect("Failed to take Network signing private key, key absent or already read");
        let signing_public = config.network_keypairs.signing_keys.public().clone();
        let identity_private = config
            .network_keypairs
            .identity_keys
            .take_private()
            .expect("Failed to take Network identity private key, key absent or already read");
        let identity_public = config.network_keypairs.identity_keys.public().clone();
        let trusted_peers = if role == RoleType::Validator {
            // for validators, trusted_peers is empty will be populated from consensus
            HashMap::new()
        } else {
            config.network_peers.peers.clone()
        };
        network_builder
            .transport(TransportType::TcpNoise(Some((
                identity_private,
                identity_public,
            ))))
            .connectivity_check_interval_ms(config.connectivity_check_interval_ms)
            .seed_peers(seed_peers)
            .trusted_peers(trusted_peers)
            .signing_keys((signing_private, signing_public))
            .discovery_interval_ms(config.discovery_interval_ms);
    } else if config.enable_encryption_and_authentication {
        let identity_private = config
            .network_keypairs
            .identity_keys
            .take_private()
            .expect("Failed to take Network identity private key, key absent or already read");
        let identity_public = config.network_keypairs.identity_keys.public().clone();
        // Even if a network end-point is permissionless, it might want to prove its identity to
        // another peer it connects to. For this, we use TCP + Noise but in a permission-less way.
        network_builder.transport(TransportType::TcpNoise(Some((
            identity_private,
            identity_public,
        ))));
    } else {
        network_builder.transport(TransportType::Tcp);
    }
    let (_listen_addr, network_provider) = network_builder.build();
    (runtime, network_provider)
}

pub fn setup_environment(node_config: &mut NodeConfig) -> LibraHandle {
    crash_handler::setup_panic_handler();

    // Some of our code uses the rayon global thread pool. Name the rayon threads so it doesn't
    // cause confusion, otherwise the threads would have their parent's name.
    rayon::ThreadPoolBuilder::new()
        .thread_name(|index| format!("rayon-global-{}", index))
        .build_global()
        .expect("Building rayon global thread pool should work.");

    let mut instant = Instant::now();
    let storage = start_storage_service(&node_config);
    debug!(
        "Storage service started in {} ms",
        instant.elapsed().as_millis()
    );

    instant = Instant::now();
    let executor = setup_executor(&node_config);
    debug!("Executor setup in {} ms", instant.elapsed().as_millis());
    let mut network_runtimes = vec![];
    let mut state_sync_network_handles = vec![];
    let mut ac_network_sender = None;
    let mut ac_network_events = vec![];
    let mut validator_network_provider = None;

    if let Some(network) = node_config.validator_network.as_mut() {
        let (runtime, mut network_provider) = setup_network(network, RoleType::Validator);
        state_sync_network_handles.push(network_provider.add_state_synchronizer(vec![
            ProtocolId::from_static(STATE_SYNCHRONIZER_DIRECT_SEND_PROTOCOL),
        ]));

        let (ac_sender, ac_events) =
            network_provider.add_admission_control(vec![ProtocolId::from_static(
                ADMISSION_CONTROL_RPC_PROTOCOL,
            )]);
        ac_network_events.push(ac_events);

        validator_network_provider = Some((network.peer_id, runtime, network_provider));
        ac_network_sender = Some(ac_sender);
    }

    for i in 0..node_config.full_node_networks.len() {
        let (runtime, mut network_provider) =
            setup_network(&mut node_config.full_node_networks[i], RoleType::FullNode);
        state_sync_network_handles.push(network_provider.add_state_synchronizer(vec![
            ProtocolId::from_static(STATE_SYNCHRONIZER_DIRECT_SEND_PROTOCOL),
        ]));

        let (ac_sender, ac_events) =
            network_provider.add_admission_control(vec![ProtocolId::from_static(
                ADMISSION_CONTROL_RPC_PROTOCOL,
            )]);
        ac_network_events.push(ac_events);

        let network = &node_config.full_node_networks[i];
        if node_config.is_upstream_network(network) {
            ac_network_sender = Some(ac_sender);
        }
        // Start the network provider.
        runtime.handle().spawn(network_provider.start());
        network_runtimes.push(runtime);
        debug!("Network started for peer_id: {}", network.peer_id);
    }

    let debug_if = ServerHandle::setup(setup_debug_interface(&node_config));

    let metrics_port = node_config.debug_interface.metrics_server_port;
    let metric_host = node_config.debug_interface.address.clone();
    thread::spawn(move || metric_server::start_server(metric_host, metrics_port, false));
    let public_metrics_port = node_config.debug_interface.public_metrics_server_port;
    let public_metric_host = node_config.debug_interface.address.clone();
    thread::spawn(move || {
        metric_server::start_server(public_metric_host, public_metrics_port, true)
    });

    let state_synchronizer = StateSynchronizer::bootstrap(
        state_sync_network_handles,
        Arc::clone(&executor),
        &node_config,
    );
    let admission_control = AdmissionControlRuntime::bootstrap(
        &node_config,
        ac_network_sender.unwrap(),
        ac_network_events,
    );

    let mut mempool = None;
    let mut consensus = None;
    if let Some((peer_id, runtime, mut network_provider)) = validator_network_provider {
        // Note: We need to start network provider before consensus, because the consensus
        // initialization is blocked on state synchronizer to sync to the initial root ledger
        // info, which in turn cannot make progress before network initialization
        // because the NewPeer events which state synchronizer uses to know its
        // peers are delivered by network provider. If we were to start network
        // provider after consensus, we create a cyclic dependency from
        // network provider -> consensus -> state synchronizer -> network provider. This deadlock
        // was observed in GitHub Issue #749. A long term fix might be make
        // consensus initialization async instead of blocking on state synchronizer.
        let (mempool_network_sender, mempool_network_events) = network_provider
            .add_mempool(vec![ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL)]);
        let (consensus_network_sender, consensus_network_events) =
            network_provider.add_consensus(vec![
                ProtocolId::from_static(CONSENSUS_RPC_PROTOCOL),
                ProtocolId::from_static(CONSENSUS_DIRECT_SEND_PROTOCOL),
            ]);
        runtime.handle().spawn(network_provider.start());
        network_runtimes.push(runtime);
        debug!("Network started for peer_id: {}", peer_id);

        // Initialize and start mempool.
        instant = Instant::now();
        mempool = Some(MempoolRuntime::bootstrap(
            &node_config,
            mempool_network_sender,
            mempool_network_events,
        ));
        debug!("Mempool started in {} ms", instant.elapsed().as_millis());

        // Initialize and start consensus.
        instant = Instant::now();
        let mut consensus_provider = make_consensus_provider(
            node_config,
            consensus_network_sender,
            consensus_network_events,
            executor,
            state_synchronizer.create_client(),
        );
        consensus_provider
            .start()
            .expect("Failed to start consensus. Can't proceed.");
        consensus = Some(consensus_provider);
        debug!("Consensus started in {} ms", instant.elapsed().as_millis());
    }

    LibraHandle {
        _network_runtimes: network_runtimes,
        _ac: admission_control,
        _mempool: mempool,
        _state_synchronizer: state_synchronizer,
        consensus,
        _storage: storage,
        _debug: debug_if,
    }
}
<commit_msg>[Tiny] Don't start consensus until state-sync is caught up<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use admission_control_service::runtime::AdmissionControlRuntime;
use consensus::consensus_provider::{make_consensus_provider, ConsensusProvider};
use debug_interface::{node_debug_service::NodeDebugService, proto::create_node_debug_interface};
use executor::Executor;
use futures::executor::block_on;
use grpc_helpers::ServerHandle;
use grpcio::EnvBuilder;
use libra_config::config::{NetworkConfig, NodeConfig, RoleType};
use libra_logger::prelude::*;
use libra_mempool::MempoolRuntime;
use libra_metrics::metric_server;
use network::{
    validator_network::{
        network_builder::{NetworkBuilder, TransportType},
        LibraNetworkProvider,
        // when you add a new protocol const, you must add this in either
        // .direct_send_protocols or .rpc_protocols vector of network_builder in setup_network()
        ADMISSION_CONTROL_RPC_PROTOCOL,
        CONSENSUS_DIRECT_SEND_PROTOCOL,
        CONSENSUS_RPC_PROTOCOL,
        MEMPOOL_DIRECT_SEND_PROTOCOL,
        STATE_SYNCHRONIZER_DIRECT_SEND_PROTOCOL,
    },
    ProtocolId,
};
use state_synchronizer::StateSynchronizer;
use std::collections::HashMap;
use std::{sync::Arc, thread, time::Instant};
use storage_client::{StorageRead, StorageReadServiceClient, StorageWriteServiceClient};
use storage_service::start_storage_service;
use tokio::runtime::{Builder, Runtime};
use vm_runtime::LibraVM;

pub struct LibraHandle {
    _ac: AdmissionControlRuntime,
    _mempool: Option<MempoolRuntime>,
    _state_synchronizer: StateSynchronizer,
    _network_runtimes: Vec<Runtime>,
    consensus: Option<Box<dyn ConsensusProvider>>,
    _storage: ServerHandle,
    _debug: ServerHandle,
}

impl Drop for LibraHandle {
    fn drop(&mut self) {
        if let Some(consensus) = &mut self.consensus {
            consensus.stop();
        }
    }
}

fn setup_executor(config: &NodeConfig) -> Arc<Executor<LibraVM>> {
    let client_env = Arc::new(EnvBuilder::new().name_prefix("grpc-exe-sto-").build());
    let storage_read_client = Arc::new(StorageReadServiceClient::new(
        Arc::clone(&client_env),
        &config.storage.address,
        config.storage.port,
    ));
    let storage_write_client = Arc::new(StorageWriteServiceClient::new(
        Arc::clone(&client_env),
        &config.storage.address,
        config.storage.port,
        config.storage.grpc_max_receive_len,
    ));

    Arc::new(Executor::new(
        Arc::clone(&storage_read_client) as Arc<dyn StorageRead>,
        storage_write_client,
        config,
    ))
}

fn setup_debug_interface(config: &NodeConfig) -> ::grpcio::Server {
    let env = Arc::new(EnvBuilder::new().name_prefix("grpc-debug-").build());
    // Start Debug interface
    let debug_service = create_node_debug_interface(NodeDebugService::new());
    ::grpcio::ServerBuilder::new(env)
        .register_service(debug_service)
        .bind(
            config.debug_interface.address.clone(),
            config.debug_interface.admission_control_node_debug_port,
        )
        .build()
        .expect("Unable to create grpc server")
}

// TODO(abhayb): Move to network crate (similar to consensus).
pub fn setup_network(
    config: &mut NetworkConfig,
    role: RoleType,
) -> (Runtime, Box<dyn LibraNetworkProvider>) {
    let runtime = Builder::new()
        .thread_name("network-")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("Failed to start runtime. Won't be able to start networking.");
    let mut network_builder = NetworkBuilder::new(
        runtime.handle().clone(),
        config.peer_id,
        config.listen_address.clone(),
        role,
    );
    network_builder
        .permissioned(config.is_permissioned)
        .advertised_address(config.advertised_address.clone())
        .direct_send_protocols(vec![
            ProtocolId::from_static(CONSENSUS_DIRECT_SEND_PROTOCOL),
            ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL),
            ProtocolId::from_static(STATE_SYNCHRONIZER_DIRECT_SEND_PROTOCOL),
        ])
        .rpc_protocols(vec![
            ProtocolId::from_static(CONSENSUS_RPC_PROTOCOL),
            ProtocolId::from_static(ADMISSION_CONTROL_RPC_PROTOCOL),
        ]);
    if config.is_permissioned {
        // If the node wants to run in permissioned mode, it should also have authentication and
        // encryption.
        assert!(
            config.enable_encryption_and_authentication,
            "Permissioned network end-points should use authentication"
        );
        let seed_peers = config.seed_peers.seed_peers.clone();
        let signing_private = config
            .network_keypairs
            .signing_keys
            .take_private()
            .expect("Failed to take Network signing private key, key absent or already read");
        let signing_public = config.network_keypairs.signing_keys.public().clone();
        let identity_private = config
            .network_keypairs
            .identity_keys
            .take_private()
            .expect("Failed to take Network identity private key, key absent or already read");
        let identity_public = config.network_keypairs.identity_keys.public().clone();
        let trusted_peers = if role == RoleType::Validator {
            // for validators, trusted_peers is empty will be populated from consensus
            HashMap::new()
        } else {
            config.network_peers.peers.clone()
        };
        network_builder
            .transport(TransportType::TcpNoise(Some((
                identity_private,
                identity_public,
            ))))
            .connectivity_check_interval_ms(config.connectivity_check_interval_ms)
            .seed_peers(seed_peers)
            .trusted_peers(trusted_peers)
            .signing_keys((signing_private, signing_public))
            .discovery_interval_ms(config.discovery_interval_ms);
    } else if config.enable_encryption_and_authentication {
        let identity_private = config
            .network_keypairs
            .identity_keys
            .take_private()
            .expect("Failed to take Network identity private key, key absent or already read");
        let identity_public = config.network_keypairs.identity_keys.public().clone();
        // Even if a network end-point is permissionless, it might want to prove its identity to
        // another peer it connects to. For this, we use TCP + Noise but in a permission-less way.
        network_builder.transport(TransportType::TcpNoise(Some((
            identity_private,
            identity_public,
        ))));
    } else {
        network_builder.transport(TransportType::Tcp);
    }
    let (_listen_addr, network_provider) = network_builder.build();
    (runtime, network_provider)
}

pub fn setup_environment(node_config: &mut NodeConfig) -> LibraHandle {
    crash_handler::setup_panic_handler();

    // Some of our code uses the rayon global thread pool. Name the rayon threads so it doesn't
    // cause confusion, otherwise the threads would have their parent's name.
    rayon::ThreadPoolBuilder::new()
        .thread_name(|index| format!("rayon-global-{}", index))
        .build_global()
        .expect("Building rayon global thread pool should work.");

    let mut instant = Instant::now();
    let storage = start_storage_service(&node_config);
    debug!(
        "Storage service started in {} ms",
        instant.elapsed().as_millis()
    );

    instant = Instant::now();
    let executor = setup_executor(&node_config);
    debug!("Executor setup in {} ms", instant.elapsed().as_millis());
    let mut network_runtimes = vec![];
    let mut state_sync_network_handles = vec![];
    let mut ac_network_sender = None;
    let mut ac_network_events = vec![];
    let mut validator_network_provider = None;

    if let Some(network) = node_config.validator_network.as_mut() {
        let (runtime, mut network_provider) = setup_network(network, RoleType::Validator);
        state_sync_network_handles.push(network_provider.add_state_synchronizer(vec![
            ProtocolId::from_static(STATE_SYNCHRONIZER_DIRECT_SEND_PROTOCOL),
        ]));

        let (ac_sender, ac_events) =
            network_provider.add_admission_control(vec![ProtocolId::from_static(
                ADMISSION_CONTROL_RPC_PROTOCOL,
            )]);
        ac_network_events.push(ac_events);

        validator_network_provider = Some((network.peer_id, runtime, network_provider));
        ac_network_sender = Some(ac_sender);
    }

    for i in 0..node_config.full_node_networks.len() {
        let (runtime, mut network_provider) =
            setup_network(&mut node_config.full_node_networks[i], RoleType::FullNode);
        state_sync_network_handles.push(network_provider.add_state_synchronizer(vec![
            ProtocolId::from_static(STATE_SYNCHRONIZER_DIRECT_SEND_PROTOCOL),
        ]));

        let (ac_sender, ac_events) =
            network_provider.add_admission_control(vec![ProtocolId::from_static(
                ADMISSION_CONTROL_RPC_PROTOCOL,
            )]);
        ac_network_events.push(ac_events);

        let network = &node_config.full_node_networks[i];
        if node_config.is_upstream_network(network) {
            ac_network_sender = Some(ac_sender);
        }
        // Start the network provider.
        runtime.handle().spawn(network_provider.start());
        network_runtimes.push(runtime);
        debug!("Network started for peer_id: {}", network.peer_id);
    }

    let debug_if = ServerHandle::setup(setup_debug_interface(&node_config));

    let metrics_port = node_config.debug_interface.metrics_server_port;
    let metric_host = node_config.debug_interface.address.clone();
    thread::spawn(move || metric_server::start_server(metric_host, metrics_port, false));
    let public_metrics_port = node_config.debug_interface.public_metrics_server_port;
    let public_metric_host = node_config.debug_interface.address.clone();
    thread::spawn(move || {
        metric_server::start_server(public_metric_host, public_metrics_port, true)
    });

    let state_synchronizer = StateSynchronizer::bootstrap(
        state_sync_network_handles,
        Arc::clone(&executor),
        &node_config,
    );
    let admission_control = AdmissionControlRuntime::bootstrap(
        &node_config,
        ac_network_sender.unwrap(),
        ac_network_events,
    );

    let mut mempool = None;
    let mut consensus = None;
    if let Some((peer_id, runtime, mut network_provider)) = validator_network_provider {
        // Note: We need to start network provider before consensus, because the consensus
        // initialization is blocked on state synchronizer to sync to the initial root ledger
        // info, which in turn cannot make progress before network initialization
        // because the NewPeer events which state synchronizer uses to know its
        // peers are delivered by network provider. If we were to start network
        // provider after consensus, we create a cyclic dependency from
        // network provider -> consensus -> state synchronizer -> network provider. This deadlock
        // was observed in GitHub Issue #749. A long term fix might be make
        // consensus initialization async instead of blocking on state synchronizer.
        let (mempool_network_sender, mempool_network_events) = network_provider
            .add_mempool(vec![ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL)]);
        let (consensus_network_sender, consensus_network_events) =
            network_provider.add_consensus(vec![
                ProtocolId::from_static(CONSENSUS_RPC_PROTOCOL),
                ProtocolId::from_static(CONSENSUS_DIRECT_SEND_PROTOCOL),
            ]);
        runtime.handle().spawn(network_provider.start());
        network_runtimes.push(runtime);
        debug!("Network started for peer_id: {}", peer_id);

        // Make sure that state synchronizer is caught up at least to its waypoint
        // (in case it's present). There is no sense to start consensus prior to that.
        // TODO: Note that we need the networking layer to be able to discover & connect to the
        // peers with potentially outdated network identity public keys.
        debug!("Wait until state synchronizer is initialized");
        block_on(state_synchronizer.wait_until_initialized())
            .expect("State synchronizer initialization failure");
        debug!("State synchronizer initialization complete.");

        // Initialize and start mempool.
        instant = Instant::now();
        mempool = Some(MempoolRuntime::bootstrap(
            &node_config,
            mempool_network_sender,
            mempool_network_events,
        ));
        debug!("Mempool started in {} ms", instant.elapsed().as_millis());

        // Initialize and start consensus.
        instant = Instant::now();
        let mut consensus_provider = make_consensus_provider(
            node_config,
            consensus_network_sender,
            consensus_network_events,
            executor,
            state_synchronizer.create_client(),
        );
        consensus_provider
            .start()
            .expect("Failed to start consensus. Can't proceed.");
        consensus = Some(consensus_provider);
        debug!("Consensus started in {} ms", instant.elapsed().as_millis());
    }

    LibraHandle {
        _network_runtimes: network_runtimes,
        _ac: admission_control,
        _mempool: mempool,
        _state_synchronizer: state_synchronizer,
        consensus,
        _storage: storage,
        _debug: debug_if,
    }
}
<|endoftext|>


<commit_before>use std::io::net::ip::ToSocketAddr;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::Arc;

use self::Kind::{Fine, Bad};

use prelude::*;
use {method, headers};
use {AfterMiddleware, BeforeMiddleware, Handler, TypeMap, Url};

#[test] fn test_chain_normal() {
    test_chain(
        (
            vec![(Fine, Fine), (Fine, Fine), (Fine, Fine)],
            (Fine, Fine),
            vec![(Fine, Fine), (Fine, Fine), (Fine, Fine)]
        ),

        (vec![Fine, Fine, Fine], Fine, vec![Fine, Fine, Fine])
    );
}

#[test] fn test_chain_before_error() {
    test_chain(
        (
            vec![
                (Bad, Fine),
                // Error in before
                (Fine, Bad),
                (Fine, Bad)
            ],
            (Fine, Fine),
            vec![
                (Fine, Bad),
                (Fine, Bad),
                (Fine, Bad)
            ]
        ),

        (vec![Fine, Bad, Bad], Bad, vec![Bad, Bad, Bad])
    );
}

#[test] fn test_chain_handler_error() {
    test_chain(
        (
            vec![
                (Fine, Fine),
                (Fine, Fine),
                (Fine, Fine)
            ],
            // Error in handler
            (Bad, Bad),
            vec![
                (Fine, Bad),
                (Fine, Bad),
                (Fine, Bad)
            ]
        ),

        (vec![Fine, Fine, Fine], Fine, vec![Bad, Bad, Bad])
    );
}

#[test] fn test_chain_after_error() {
    test_chain(
        (
            vec![
                (Fine, Fine),
                (Fine, Fine),
                (Fine, Fine)
            ],
            (Fine, Fine),
            vec![
                // Error in after
                (Bad, Fine),
                (Fine, Bad),
                (Fine, Bad)
            ]
        ),

        (vec![Fine, Fine, Fine], Fine, vec![Fine, Bad, Bad])
    );
}

#[test] fn test_chain_before_error_then_handle() {
    test_chain(
        (
            vec![
                (Bad, Fine),
                (Fine, Bad),
                // Handle in before middleware
                (Fine, Fine),
                (Fine, Fine)
            ],
            (Fine, Fine),
            vec![(Fine, Fine)]
        ),

        (vec![Fine, Bad, Bad, Fine], Fine, vec![Fine])
    );
}

#[test] fn test_chain_after_error_then_handle() {
    test_chain(
        (
            vec![],
            (Fine, Fine),
            vec![
                // Error and handle in after middleware
                (Bad, Fine),
                (Fine, Bad),
                (Fine, Fine),
                (Fine, Fine)
            ]
        ),

        (vec![], Fine, vec![Fine, Bad, Bad, Fine])
    );
}

#[test] fn test_chain_handler_error_then_handle() {
    test_chain(
        (
            vec![],
            // Error in handler.
            (Bad, Fine),
            vec![
                (Fine, Bad),
                (Fine, Fine),
                (Fine, Fine),
            ]
        ),

        (vec![], Fine, vec![Bad, Bad, Fine])
    );
}

// Used to indicate the action taken by a middleware or handler.
#[derive(Debug, PartialEq)]
enum Kind {
    Fine,
    Bad
}

struct Middleware {
    normal: Arc<AtomicBool>,
    error: Arc<AtomicBool>,
    normal_action: Kind,
    error_action: Kind
}

impl BeforeMiddleware for Middleware {
    fn before(&self, _: &mut Request) -> IronResult<()> {
        assert!(!self.normal.load(Relaxed));
        self.normal.store(true, Relaxed);

        match self.normal_action {
            Fine => { Ok(()) },
            Bad => { Err(error()) }
        }
    }

    fn catch(&self, _: &mut Request, _: IronError) -> IronResult<()> {
        assert!(!self.error.load(Relaxed));
        self.error.store(true, Relaxed);

        match self.error_action {
            Fine => { Ok(()) },
            Bad => { Err(error()) },
        }
    }
}

impl Handler for Middleware {
    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        assert!(!self.normal.load(Relaxed));
        self.normal.store(true, Relaxed);

        match self.normal_action {
            Fine => { Ok(response()) },
            Bad => { Err(error()) }
        }
    }
}

impl AfterMiddleware for Middleware {
    fn after(&self, _: &mut Request, _: Response) -> IronResult<Response> {
        assert!(!self.normal.load(Relaxed));
        self.normal.store(true, Relaxed);

        match self.normal_action {
            Fine => { Ok(response()) },
            Bad => { Err(error()) }
        }
    }

    fn catch(&self, _: &mut Request, _: IronError) -> IronResult<Response> {
        assert!(!self.error.load(Relaxed));
        self.error.store(true, Relaxed);

        match self.error_action {
            Fine => { Ok(response()) },
            Bad => { Err(error()) },
        }
    }
}

// Stub request
fn request() -> Request {
    Request {
        url: Url::parse("http://www.rust-lang.org").unwrap(),
        remote_addr: "localhost:3000".to_socket_addr().unwrap(),
        local_addr: "localhost:3000".to_socket_addr().unwrap(),
        headers: headers::Headers::new(),
        body: vec![],
        method: method::Get,
        extensions: TypeMap::new()
    }
}

// Stub response
fn response() -> Response { Response::new() }

// Stub error
fn error() -> IronError {
    use std::fmt::{self, Debug, Display};
    use std::error::Error as StdError;

    #[derive(Debug)]
    struct SomeError;

    impl Display for SomeError {
        fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            Debug::fmt(self, fmt)
        }
    }

    impl StdError for SomeError {
        fn description(&self) -> &str { "Some Error" }
    }

    IronError {
        error: Box::new(SomeError),
        response: response()
    }
}

type ChainLike<T> = (Vec<T>, T, Vec<T>);
type Twice<T> = (T, T);

fn sharedbool(val: bool) -> Arc<AtomicBool> {
    Arc::new(AtomicBool::new(val))
}

fn counters(chain: &ChainLike<Twice<Kind>>) -> ChainLike<Twice<Arc<AtomicBool>>> {
    let (ref befores, _, ref afters) = *chain;

    (
        befores.iter()
            .map(|_| (sharedbool(false), sharedbool(false)))
            .collect::<Vec<_>>(),

        (sharedbool(false), sharedbool(false)),

        afters.iter()
            .map(|_| (sharedbool(false), sharedbool(false)))
            .collect::<Vec<_>>()
    )
}

fn to_chain(counters: &ChainLike<Twice<Arc<AtomicBool>>>,
            chain: ChainLike<Twice<Kind>>) -> Chain {
    let (befores, handler, afters) = chain;
    let (ref beforec, ref handlerc, ref afterc) = *counters;

    let befores = befores.into_iter().zip(beforec.iter())
        .map(into_middleware)
        .map(|&: m| box m as Box<BeforeMiddleware>)
        .collect::<Vec<_>>();

    let handler = into_middleware((handler, handlerc));

    let afters = afters.into_iter().zip(afterc.iter())
        .map(into_middleware)
        .map(|&: m| box m as Box<AfterMiddleware>)
        .collect::<Vec<_>>();

    Chain {
        befores: befores,
        handler: Some(box handler as Box<Handler>),
        afters: afters
    }
}

fn into_middleware(input: (Twice<Kind>, &Twice<Arc<AtomicBool>>)) -> Middleware {
    let (normal, error) = input.0;
    let (ref normalc, ref errorc) = *input.1;

    Middleware {
        normal: normalc.clone(),
        error: errorc.clone(),
        normal_action: normal,
        error_action: error
    }
}

fn to_kind(val: bool) -> Kind {
    if val { Fine } else { Bad }
}

fn test_chain(chain: ChainLike<(Kind, Kind)>, expected: ChainLike<Kind>) {
    let actual = counters(&chain);
    let chain = to_chain(&actual, chain);

    // Run the chain
    let _ = chain.handle(&mut request());

    // Get all the results
    let outbefores = actual.0.into_iter()
        .map(|(normal, _)| to_kind(normal.load(Relaxed))).collect::<Vec<_>>();

    let outhandler = to_kind((actual.1).0.load(Relaxed));

    let outafters = actual.2.into_iter()
        .map(|(normal, _)| to_kind(normal.load(Relaxed))).collect::<Vec<_>>();

    let outchain = (outbefores, outhandler, outafters);

    // Yay! Actually do the test!
    assert_eq!(outchain, expected);
}

<commit_msg>(test) Simplify the chain testing infrastructure.<commit_after>use std::io::net::ip::ToSocketAddr;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::Arc;

use self::Kind::{Fine, Prob};

use prelude::*;
use {method, headers};
use {AfterMiddleware, BeforeMiddleware, Handler, TypeMap, Url};

#[test] fn test_chain_normal() {
    test_chain(
        (vec![Fine, Fine, Fine], Fine, vec![Fine, Fine, Fine]),
        (vec![Fine, Fine, Fine], Fine, vec![Fine, Fine, Fine])
    );
}

#[test] fn test_chain_before_error() {
    test_chain(
        // Error in before
        (vec![Prob, Prob, Prob], Fine, vec![Prob, Prob, Prob]),
        (vec![Fine, Prob, Prob], Prob, vec![Prob, Prob, Prob])
    );
}

#[test] fn test_chain_handler_error() {
    test_chain(
        // Error in handler
        (vec![Fine, Fine, Fine], Prob, vec![Prob, Prob, Prob]),
        (vec![Fine, Fine, Fine], Fine, vec![Prob, Prob, Prob])
    );
}

#[test] fn test_chain_after_error() {
    test_chain(
        // Error in after
        (vec![Fine, Fine, Fine], Fine, vec![Prob, Prob, Prob]),
        (vec![Fine, Fine, Fine], Fine, vec![Fine, Prob, Prob])
    );
}

#[test] fn test_chain_before_error_then_handle() {
    test_chain(
        // Error and handle in before middleware
        (vec![Prob, Prob, Fine, Fine], Fine, vec![Fine]),
        (vec![Fine, Prob, Prob, Fine], Fine, vec![Fine])
    );
}

#[test] fn test_chain_after_error_then_handle() {
    test_chain(
        // Error and handle in after middleware
        (vec![], Fine, vec![Prob, Prob, Fine, Fine]),
        (vec![], Fine, vec![Fine, Prob, Prob, Fine])
    );
}

#[test] fn test_chain_handler_error_then_handle() {
    test_chain(
        // Error in handler.
        (vec![], Prob, vec![Prob, Fine, Fine]),
        (vec![], Fine, vec![Prob, Prob, Fine])
    );
}

// Used to indicate the action taken by a middleware or handler.
#[derive(Debug, PartialEq)]
enum Kind {
    Fine,
    Prob
}

struct Middleware {
    normal: Arc<AtomicBool>,
    error: Arc<AtomicBool>,
    mode: Kind
}

impl BeforeMiddleware for Middleware {
    fn before(&self, _: &mut Request) -> IronResult<()> {
        assert!(!self.normal.load(Relaxed));
        self.normal.store(true, Relaxed);

        match self.mode {
            Fine => { Ok(()) },
            Prob => { Err(error()) }
        }
    }

    fn catch(&self, _: &mut Request, _: IronError) -> IronResult<()> {
        assert!(!self.error.load(Relaxed));
        self.error.store(true, Relaxed);

        match self.mode {
            Fine => { Ok(()) },
            Prob => { Err(error()) },
        }
    }
}

impl Handler for Middleware {
    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        assert!(!self.normal.load(Relaxed));
        self.normal.store(true, Relaxed);

        match self.mode {
            Fine => { Ok(response()) },
            Prob => { Err(error()) }
        }
    }
}

impl AfterMiddleware for Middleware {
    fn after(&self, _: &mut Request, _: Response) -> IronResult<Response> {
        assert!(!self.normal.load(Relaxed));
        self.normal.store(true, Relaxed);

        match self.mode {
            Fine => { Ok(response()) },
            Prob => { Err(error()) }
        }
    }

    fn catch(&self, _: &mut Request, _: IronError) -> IronResult<Response> {
        assert!(!self.error.load(Relaxed));
        self.error.store(true, Relaxed);

        match self.mode {
            Fine => { Ok(response()) },
            Prob => { Err(error()) },
        }
    }
}

// Stub request
fn request() -> Request {
    Request {
        url: Url::parse("http://www.rust-lang.org").unwrap(),
        remote_addr: "localhost:3000".to_socket_addr().unwrap(),
        local_addr: "localhost:3000".to_socket_addr().unwrap(),
        headers: headers::Headers::new(),
        body: vec![],
        method: method::Get,
        extensions: TypeMap::new()
    }
}

// Stub response
fn response() -> Response { Response::new() }

// Stub error
fn error() -> IronError {
    use std::fmt::{self, Debug, Display};
    use std::error::Error as StdError;

    #[derive(Debug)]
    struct SomeError;

    impl Display for SomeError {
        fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            Debug::fmt(self, fmt)
        }
    }

    impl StdError for SomeError {
        fn description(&self) -> &str { "Some Error" }
    }

    IronError {
        error: Box::new(SomeError),
        response: response()
    }
}

type ChainLike<T> = (Vec<T>, T, Vec<T>);
type Twice<T> = (T, T);

fn sharedbool(val: bool) -> Arc<AtomicBool> {
    Arc::new(AtomicBool::new(val))
}

fn counters(chain: &ChainLike<Kind>) -> ChainLike<Twice<Arc<AtomicBool>>> {
    let (ref befores, _, ref afters) = *chain;

    (
        befores.iter()
            .map(|_| (sharedbool(false), sharedbool(false)))
            .collect::<Vec<_>>(),

        (sharedbool(false), sharedbool(false)),

        afters.iter()
            .map(|_| (sharedbool(false), sharedbool(false)))
            .collect::<Vec<_>>()
    )
}

fn to_chain(counters: &ChainLike<Twice<Arc<AtomicBool>>>,
            chain: ChainLike<Kind>) -> Chain {
    let (befores, handler, afters) = chain;
    let (ref beforec, ref handlerc, ref afterc) = *counters;

    let befores = befores.into_iter().zip(beforec.iter())
        .map(into_middleware)
        .map(|&: m| box m as Box<BeforeMiddleware>)
        .collect::<Vec<_>>();

    let handler = into_middleware((handler, handlerc));

    let afters = afters.into_iter().zip(afterc.iter())
        .map(into_middleware)
        .map(|&: m| box m as Box<AfterMiddleware>)
        .collect::<Vec<_>>();

    Chain {
        befores: befores,
        handler: Some(box handler as Box<Handler>),
        afters: afters
    }
}

fn into_middleware(input: (Kind, &Twice<Arc<AtomicBool>>)) -> Middleware {
    let mode = input.0;
    let (ref normal, ref error) = *input.1;

    Middleware {
        normal: normal.clone(),
        error: error.clone(),
        mode: mode
    }
}

fn to_kind(val: bool) -> Kind {
    if val { Fine } else { Prob }
}

fn test_chain(chain: ChainLike<Kind>, expected: ChainLike<Kind>) {
    let actual = counters(&chain);
    let chain = to_chain(&actual, chain);

    // Run the chain
    let _ = chain.handle(&mut request());

    // Get all the results
    let outbefores = actual.0.into_iter()
        .map(|(normal, _)| to_kind(normal.load(Relaxed))).collect::<Vec<_>>();

    let outhandler = to_kind((actual.1).0.load(Relaxed));

    let outafters = actual.2.into_iter()
        .map(|(normal, _)| to_kind(normal.load(Relaxed))).collect::<Vec<_>>();

    let outchain = (outbefores, outhandler, outafters);

    // Yay! Actually do the test!
    assert_eq!(outchain, expected);
}

<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{
    conversion::{api::UnanalyzedApi, codegen_cpp::type_to_cpp::type_to_cpp, ConvertError},
    known_types::known_types,
    types::{make_ident, Namespace, QualifiedName},
};
use quote::ToTokens;
use std::collections::{HashMap, HashSet};
use syn::{
    parse_quote, punctuated::Punctuated, GenericArgument, PathArguments, PathSegment, Type,
    TypePath, TypePtr,
};

/// Results of some type conversion, annotated with a list of every type encountered,
/// and optionally any extra APIs we need in order to use this type.
pub(crate) struct Annotated<T> {
    pub(crate) ty: T,
    pub(crate) types_encountered: HashSet<QualifiedName>,
    pub(crate) extra_apis: Vec<UnanalyzedApi>,
    pub(crate) requires_unsafe: bool,
}

impl<T> Annotated<T> {
    fn new(
        ty: T,
        types_encountered: HashSet<QualifiedName>,
        extra_apis: Vec<UnanalyzedApi>,
        requires_unsafe: bool,
    ) -> Self {
        Self {
            ty,
            types_encountered,
            extra_apis,
            requires_unsafe,
        }
    }

    fn map<T2, F: FnOnce(T) -> T2>(self, fun: F) -> Annotated<T2> {
        Annotated {
            ty: fun(self.ty),
            types_encountered: self.types_encountered,
            extra_apis: self.extra_apis,
            requires_unsafe: self.requires_unsafe,
        }
    }
}

/// A type which can convert from a type encountered in `bindgen`
/// output to the sort of type we should represeent to `cxx`.
/// As a simple example, `std::string` should be replaced
/// with [CxxString]. This also involves keeping track
/// of typedefs, and any instantiated concrete types.
///
/// This object is a bit of a pest. The information here
/// is compiled during the parsing phase (which is why it lives
/// in the parse mod) but is used during various other phases.
/// As such it contributes to both the parsing and analysis phases.
/// It's possible that the information here largely duplicates
/// information stored elsewhere in the list of `Api`s, or can
/// easily be moved into it, which would enable us to
/// distribute this logic elsewhere.
pub(crate) struct TypeConverter {
    types_found: Vec<QualifiedName>,
    typedefs: HashMap<QualifiedName, Type>,
    concrete_templates: HashMap<String, QualifiedName>,
}

impl TypeConverter {
    pub(crate) fn new() -> Self {
        Self {
            types_found: Vec::new(),
            typedefs: HashMap::new(),
            concrete_templates: HashMap::new(),
        }
    }

    pub(crate) fn push(&mut self, ty: QualifiedName) {
        self.types_found.push(ty);
    }

    pub(crate) fn insert_typedef(&mut self, id: QualifiedName, target: Type) {
        self.typedefs.insert(id, target);
    }

    pub(crate) fn convert_boxed_type(
        &mut self,
        ty: Box<Type>,
        ns: &Namespace,
        convert_ptrs_to_reference: bool,
    ) -> Result<Annotated<Box<Type>>, ConvertError> {
        Ok(self
            .convert_type(*ty, ns, convert_ptrs_to_reference)?
            .map(Box::new))
    }

    pub(crate) fn convert_type(
        &mut self,
        ty: Type,
        ns: &Namespace,
        convert_ptrs_to_reference: bool,
    ) -> Result<Annotated<Type>, ConvertError> {
        let result = match ty {
            Type::Path(p) => {
                let newp = self.convert_type_path(p, ns)?;
                if let Type::Path(newpp) = &newp.ty {
                    // Special handling because rust_Str (as emitted by bindgen)
                    // doesn't simply get renamed to a different type _identifier_.
                    // This plain type-by-value (as far as bindgen is concerned)
                    // is actually a &str.
                    if known_types().should_dereference_in_cpp(newpp) {
                        Annotated::new(
                            Type::Reference(parse_quote! {
                                &str
                            }),
                            newp.types_encountered,
                            newp.extra_apis,
                            false,
                        )
                    } else {
                        newp
                    }
                } else {
                    newp
                }
            }
            Type::Reference(mut r) => {
                let innerty = self.convert_boxed_type(r.elem, ns, false)?;
                r.elem = innerty.ty;
                Annotated::new(
                    Type::Reference(r),
                    innerty.types_encountered,
                    innerty.extra_apis,
                    false,
                )
            }
            Type::Ptr(ptr) if convert_ptrs_to_reference => {
                self.convert_ptr_to_reference(ptr, ns)?
            }
            Type::Ptr(mut ptr) => {
                crate::known_types::ensure_pointee_is_valid(&ptr)?;
                let innerty = self.convert_boxed_type(ptr.elem, ns, false)?;
                ptr.elem = innerty.ty;
                Annotated::new(
                    Type::Ptr(ptr),
                    innerty.types_encountered,
                    innerty.extra_apis,
                    true,
                )
            }
            _ => return Err(ConvertError::UnknownType(ty.to_token_stream().to_string())),
        };
        Ok(result)
    }

    fn convert_type_path(
        &mut self,
        mut typ: TypePath,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        // First, qualify any unqualified paths.
        if typ.path.segments.iter().next().unwrap().ident != "root" {
            let ty = QualifiedName::from_type_path(&typ);
            // If the type looks like it is unqualified, check we know it
            // already, and if not, qualify it according to the current
            // namespace. This is a bit of a shortcut compared to having a full
            // resolution pass which can search all known namespaces.
            if !known_types().is_known_type(&ty) {
                let num_segments = typ.path.segments.len();
                if num_segments > 1 {
                    return Err(ConvertError::UnsupportedBuiltInType(ty));
                }
                if !self.types_found.contains(&ty) {
                    typ.path.segments = std::iter::once(&"root".to_string())
                        .chain(ns.iter())
                        .map(|s| {
                            let i = make_ident(s);
                            parse_quote! { #i }
                        })
                        .chain(typ.path.segments.into_iter())
                        .collect();
                }
            }
        }

        let original_tn = QualifiedName::from_type_path(&typ);
        let mut deps = HashSet::new();

        // Now convert this type itself.
        deps.insert(original_tn.clone());
        // First let's see if this is a typedef.
        let (typ, tn) = match self.resolve_typedef(&original_tn) {
            None => (typ, original_tn),
            Some(Type::Path(resolved_tp)) => {
                let resolved_tn = QualifiedName::from_type_path(&resolved_tp);
                deps.insert(resolved_tn.clone());
                (resolved_tp.clone(), resolved_tn)
            }
            Some(other) => {
                return Ok(Annotated::new(
                    other.clone(),
                    HashSet::new(),
                    Vec::new(),
                    false,
                ))
            }
        };

        // Now let's see if it's a known type.
        // (We may entirely reject some types at this point too.)
        let mut typ = match known_types().consider_substitution(&tn)? {
            Some(mut substitute_type) => {
                if let Some(last_seg_args) =
                    typ.path.segments.into_iter().last().map(|ps| ps.arguments)
                {
                    let last_seg = substitute_type.path.segments.last_mut().unwrap();
                    last_seg.arguments = last_seg_args;
                }
                substitute_type
            }
            None => typ,
        };

        let mut extra_apis = Vec::new();

        // Finally let's see if it's generic.
        if let Some(last_seg) = Self::get_generic_args(&mut typ) {
            if known_types().is_cxx_acceptable_generic(&tn) {
                // this is a type of generic understood by cxx (e.g. CxxVector)
                // so let's convert any generic type arguments. This recurses.
                crate::known_types::confirm_inner_type_is_acceptable_generic_payload(
                    &last_seg.arguments,
                    &tn,
                )?;
                if let PathArguments::AngleBracketed(ref mut ab) = last_seg.arguments {
                    let mut innerty = self.convert_punctuated(ab.args.clone(), ns)?;
                    ab.args = innerty.ty;
                    deps.extend(innerty.types_encountered.drain());
                }
            } else {
                // Oh poop. It's a generic type which cxx won't be able to handle.
                // We'll have to come up with a concrete type in both the cxx::bridge (in Rust)
                // and a corresponding typedef in C++.
                let (new_tn, api) = self.get_templated_typename(&Type::Path(typ))?;
                extra_apis.extend(api.into_iter());
                deps.remove(&tn);
                typ = new_tn.to_type_path();
                deps.insert(new_tn);
            }
        }
        Ok(Annotated::new(Type::Path(typ), deps, extra_apis, false))
    }

    fn get_generic_args(typ: &mut TypePath) -> Option<&mut PathSegment> {
        match typ.path.segments.last_mut() {
            Some(s) if !s.arguments.is_empty() => Some(s),
            _ => None,
        }
    }

    fn convert_punctuated<P>(
        &mut self,
        pun: Punctuated<GenericArgument, P>,
        ns: &Namespace,
    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertError>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        let mut types_encountered = HashSet::new();
        let mut extra_apis = Vec::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => {
                    let mut innerty = self.convert_type(t, ns, false)?;
                    types_encountered.extend(innerty.types_encountered.drain());
                    extra_apis.extend(innerty.extra_apis.drain(..));
                    GenericArgument::Type(innerty.ty)
                }
                _ => arg,
            });
        }
        Ok(Annotated::new(
            new_pun,
            types_encountered,
            extra_apis,
            false,
        ))
    }

    fn resolve_typedef<'b>(&'b self, tn: &QualifiedName) -> Option<&'b Type> {
        self.typedefs.get(&tn).map(|resolution| match resolution {
            Type::Path(typ) => {
                let tn = QualifiedName::from_type_path(typ);
                self.resolve_typedef(&tn).unwrap_or(resolution)
            }
            _ => resolution,
        })
    }

    fn convert_ptr_to_reference(
        &mut self,
        ptr: TypePtr,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let mutability = ptr.mutability;
        let elem = self.convert_boxed_type(ptr.elem, ns, false)?;
        // TODO - in the future, we should check if this is a rust::Str and throw
        // a wobbler if not. rust::Str should only be seen _by value_ in C++
        // headers; it manifests as &str in Rust but on the C++ side it must
        // be a plain value. We should detect and abort.
        Ok(elem.map(|elem| match mutability {
            Some(_) => Type::Path(parse_quote! {
                std::pin::Pin < & #mutability #elem >
            }),
            None => Type::Reference(parse_quote! {
                & #elem
            }),
        }))
    }

    fn add_concrete_type(&self, name: &QualifiedName, rs_definition: &Type) -> UnanalyzedApi {
        UnanalyzedApi {
            name: name.clone(),
            deps: HashSet::new(),
            detail: crate::conversion::api::ApiDetail::ConcreteType {
                rs_definition: Box::new(rs_definition.clone()),
            },
        }
    }

    fn get_templated_typename(
        &mut self,
        rs_definition: &Type,
    ) -> Result<(QualifiedName, Option<UnanalyzedApi>), ConvertError> {
        let count = self.concrete_templates.len();
        // We just use this as a hash key, essentially.
        let cpp_definition = type_to_cpp(rs_definition)?;
        let e = self.concrete_templates.get(&cpp_definition);
        match e {
            Some(tn) => Ok((tn.clone(), None)),
            None => {
                let tn = QualifiedName::new(
                    &Namespace::new(),
                    make_ident(&format!("AutocxxConcrete{}", count)),
                );
                self.concrete_templates
                    .insert(cpp_definition.clone(), tn.clone());
                let api = self.add_concrete_type(&tn, rs_definition);
                Ok((tn, Some(api)))
            }
        }
    }
}
<commit_msg>Inline a function.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{
    conversion::{api::UnanalyzedApi, codegen_cpp::type_to_cpp::type_to_cpp, ConvertError},
    known_types::known_types,
    types::{make_ident, Namespace, QualifiedName},
};
use quote::ToTokens;
use std::collections::{HashMap, HashSet};
use syn::{
    parse_quote, punctuated::Punctuated, GenericArgument, PathArguments, PathSegment, Type,
    TypePath, TypePtr,
};

/// Results of some type conversion, annotated with a list of every type encountered,
/// and optionally any extra APIs we need in order to use this type.
pub(crate) struct Annotated<T> {
    pub(crate) ty: T,
    pub(crate) types_encountered: HashSet<QualifiedName>,
    pub(crate) extra_apis: Vec<UnanalyzedApi>,
    pub(crate) requires_unsafe: bool,
}

impl<T> Annotated<T> {
    fn new(
        ty: T,
        types_encountered: HashSet<QualifiedName>,
        extra_apis: Vec<UnanalyzedApi>,
        requires_unsafe: bool,
    ) -> Self {
        Self {
            ty,
            types_encountered,
            extra_apis,
            requires_unsafe,
        }
    }

    fn map<T2, F: FnOnce(T) -> T2>(self, fun: F) -> Annotated<T2> {
        Annotated {
            ty: fun(self.ty),
            types_encountered: self.types_encountered,
            extra_apis: self.extra_apis,
            requires_unsafe: self.requires_unsafe,
        }
    }
}

/// A type which can convert from a type encountered in `bindgen`
/// output to the sort of type we should represeent to `cxx`.
/// As a simple example, `std::string` should be replaced
/// with [CxxString]. This also involves keeping track
/// of typedefs, and any instantiated concrete types.
///
/// This object is a bit of a pest. The information here
/// is compiled during the parsing phase (which is why it lives
/// in the parse mod) but is used during various other phases.
/// As such it contributes to both the parsing and analysis phases.
/// It's possible that the information here largely duplicates
/// information stored elsewhere in the list of `Api`s, or can
/// easily be moved into it, which would enable us to
/// distribute this logic elsewhere.
pub(crate) struct TypeConverter {
    types_found: Vec<QualifiedName>,
    typedefs: HashMap<QualifiedName, Type>,
    concrete_templates: HashMap<String, QualifiedName>,
}

impl TypeConverter {
    pub(crate) fn new() -> Self {
        Self {
            types_found: Vec::new(),
            typedefs: HashMap::new(),
            concrete_templates: HashMap::new(),
        }
    }

    pub(crate) fn push(&mut self, ty: QualifiedName) {
        self.types_found.push(ty);
    }

    pub(crate) fn insert_typedef(&mut self, id: QualifiedName, target: Type) {
        self.typedefs.insert(id, target);
    }

    pub(crate) fn convert_boxed_type(
        &mut self,
        ty: Box<Type>,
        ns: &Namespace,
        convert_ptrs_to_reference: bool,
    ) -> Result<Annotated<Box<Type>>, ConvertError> {
        Ok(self
            .convert_type(*ty, ns, convert_ptrs_to_reference)?
            .map(Box::new))
    }

    pub(crate) fn convert_type(
        &mut self,
        ty: Type,
        ns: &Namespace,
        convert_ptrs_to_reference: bool,
    ) -> Result<Annotated<Type>, ConvertError> {
        let result = match ty {
            Type::Path(p) => {
                let newp = self.convert_type_path(p, ns)?;
                if let Type::Path(newpp) = &newp.ty {
                    // Special handling because rust_Str (as emitted by bindgen)
                    // doesn't simply get renamed to a different type _identifier_.
                    // This plain type-by-value (as far as bindgen is concerned)
                    // is actually a &str.
                    if known_types().should_dereference_in_cpp(newpp) {
                        Annotated::new(
                            Type::Reference(parse_quote! {
                                &str
                            }),
                            newp.types_encountered,
                            newp.extra_apis,
                            false,
                        )
                    } else {
                        newp
                    }
                } else {
                    newp
                }
            }
            Type::Reference(mut r) => {
                let innerty = self.convert_boxed_type(r.elem, ns, false)?;
                r.elem = innerty.ty;
                Annotated::new(
                    Type::Reference(r),
                    innerty.types_encountered,
                    innerty.extra_apis,
                    false,
                )
            }
            Type::Ptr(ptr) if convert_ptrs_to_reference => {
                self.convert_ptr_to_reference(ptr, ns)?
            }
            Type::Ptr(mut ptr) => {
                crate::known_types::ensure_pointee_is_valid(&ptr)?;
                let innerty = self.convert_boxed_type(ptr.elem, ns, false)?;
                ptr.elem = innerty.ty;
                Annotated::new(
                    Type::Ptr(ptr),
                    innerty.types_encountered,
                    innerty.extra_apis,
                    true,
                )
            }
            _ => return Err(ConvertError::UnknownType(ty.to_token_stream().to_string())),
        };
        Ok(result)
    }

    fn convert_type_path(
        &mut self,
        mut typ: TypePath,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        // First, qualify any unqualified paths.
        if typ.path.segments.iter().next().unwrap().ident != "root" {
            let ty = QualifiedName::from_type_path(&typ);
            // If the type looks like it is unqualified, check we know it
            // already, and if not, qualify it according to the current
            // namespace. This is a bit of a shortcut compared to having a full
            // resolution pass which can search all known namespaces.
            if !known_types().is_known_type(&ty) {
                let num_segments = typ.path.segments.len();
                if num_segments > 1 {
                    return Err(ConvertError::UnsupportedBuiltInType(ty));
                }
                if !self.types_found.contains(&ty) {
                    typ.path.segments = std::iter::once(&"root".to_string())
                        .chain(ns.iter())
                        .map(|s| {
                            let i = make_ident(s);
                            parse_quote! { #i }
                        })
                        .chain(typ.path.segments.into_iter())
                        .collect();
                }
            }
        }

        let original_tn = QualifiedName::from_type_path(&typ);
        let mut deps = HashSet::new();

        // Now convert this type itself.
        deps.insert(original_tn.clone());
        // First let's see if this is a typedef.
        let (typ, tn) = match self.resolve_typedef(&original_tn) {
            None => (typ, original_tn),
            Some(Type::Path(resolved_tp)) => {
                let resolved_tn = QualifiedName::from_type_path(&resolved_tp);
                deps.insert(resolved_tn.clone());
                (resolved_tp.clone(), resolved_tn)
            }
            Some(other) => {
                return Ok(Annotated::new(
                    other.clone(),
                    HashSet::new(),
                    Vec::new(),
                    false,
                ))
            }
        };

        // Now let's see if it's a known type.
        // (We may entirely reject some types at this point too.)
        let mut typ = match known_types().consider_substitution(&tn)? {
            Some(mut substitute_type) => {
                if let Some(last_seg_args) =
                    typ.path.segments.into_iter().last().map(|ps| ps.arguments)
                {
                    let last_seg = substitute_type.path.segments.last_mut().unwrap();
                    last_seg.arguments = last_seg_args;
                }
                substitute_type
            }
            None => typ,
        };

        let mut extra_apis = Vec::new();

        // Finally let's see if it's generic.
        if let Some(last_seg) = Self::get_generic_args(&mut typ) {
            if known_types().is_cxx_acceptable_generic(&tn) {
                // this is a type of generic understood by cxx (e.g. CxxVector)
                // so let's convert any generic type arguments. This recurses.
                crate::known_types::confirm_inner_type_is_acceptable_generic_payload(
                    &last_seg.arguments,
                    &tn,
                )?;
                if let PathArguments::AngleBracketed(ref mut ab) = last_seg.arguments {
                    let mut innerty = self.convert_punctuated(ab.args.clone(), ns)?;
                    ab.args = innerty.ty;
                    deps.extend(innerty.types_encountered.drain());
                }
            } else {
                // Oh poop. It's a generic type which cxx won't be able to handle.
                // We'll have to come up with a concrete type in both the cxx::bridge (in Rust)
                // and a corresponding typedef in C++.
                let (new_tn, api) = self.get_templated_typename(&Type::Path(typ))?;
                extra_apis.extend(api.into_iter());
                deps.remove(&tn);
                typ = new_tn.to_type_path();
                deps.insert(new_tn);
            }
        }
        Ok(Annotated::new(Type::Path(typ), deps, extra_apis, false))
    }

    fn get_generic_args(typ: &mut TypePath) -> Option<&mut PathSegment> {
        match typ.path.segments.last_mut() {
            Some(s) if !s.arguments.is_empty() => Some(s),
            _ => None,
        }
    }

    fn convert_punctuated<P>(
        &mut self,
        pun: Punctuated<GenericArgument, P>,
        ns: &Namespace,
    ) -> Result<Annotated<Punctuated<GenericArgument, P>>, ConvertError>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        let mut types_encountered = HashSet::new();
        let mut extra_apis = Vec::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => {
                    let mut innerty = self.convert_type(t, ns, false)?;
                    types_encountered.extend(innerty.types_encountered.drain());
                    extra_apis.extend(innerty.extra_apis.drain(..));
                    GenericArgument::Type(innerty.ty)
                }
                _ => arg,
            });
        }
        Ok(Annotated::new(
            new_pun,
            types_encountered,
            extra_apis,
            false,
        ))
    }

    fn resolve_typedef<'b>(&'b self, tn: &QualifiedName) -> Option<&'b Type> {
        self.typedefs.get(&tn).map(|resolution| match resolution {
            Type::Path(typ) => {
                let tn = QualifiedName::from_type_path(typ);
                self.resolve_typedef(&tn).unwrap_or(resolution)
            }
            _ => resolution,
        })
    }

    fn convert_ptr_to_reference(
        &mut self,
        ptr: TypePtr,
        ns: &Namespace,
    ) -> Result<Annotated<Type>, ConvertError> {
        let mutability = ptr.mutability;
        let elem = self.convert_boxed_type(ptr.elem, ns, false)?;
        // TODO - in the future, we should check if this is a rust::Str and throw
        // a wobbler if not. rust::Str should only be seen _by value_ in C++
        // headers; it manifests as &str in Rust but on the C++ side it must
        // be a plain value. We should detect and abort.
        Ok(elem.map(|elem| match mutability {
            Some(_) => Type::Path(parse_quote! {
                std::pin::Pin < & #mutability #elem >
            }),
            None => Type::Reference(parse_quote! {
                & #elem
            }),
        }))
    }

    fn get_templated_typename(
        &mut self,
        rs_definition: &Type,
    ) -> Result<(QualifiedName, Option<UnanalyzedApi>), ConvertError> {
        let count = self.concrete_templates.len();
        // We just use this as a hash key, essentially.
        let cpp_definition = type_to_cpp(rs_definition)?;
        let e = self.concrete_templates.get(&cpp_definition);
        match e {
            Some(tn) => Ok((tn.clone(), None)),
            None => {
                let name = QualifiedName::new(
                    &Namespace::new(),
                    make_ident(&format!("AutocxxConcrete{}", count)),
                );
                self.concrete_templates
                    .insert(cpp_definition.clone(), name.clone());
                let api = UnanalyzedApi {
                    name: name.clone(),
                    deps: HashSet::new(),
                    detail: crate::conversion::api::ApiDetail::ConcreteType {
                        rs_definition: Box::new(rs_definition.clone()),
                    },
                };
                Ok((name, Some(api)))
            }
        }
    }
}
<|endoftext|>


<commit_before>use traits::Object;

/// Interface for save conversions between types.
///
/// This trait is required by `Object::convert_to()` function.
///
/// All built-in types like `Hash`, `RString` etc implement it.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate ruru;
///
/// use ruru::{Class, Fixnum, VM};
/// use ruru::traits::{Object, VerifiedObject};
///
/// class!(Server);
///
/// impl VerifiedObject for Server {
///     fn is_correct_type<T: Object>(object: &T) -> bool {
///         object.class() == Class::from_existing("Server")
///     }
///
///     fn error_message() -> String {
///         "Error converting to Server".to_string()
///     }
/// }
///
/// fn main() {
/// }
/// ```
pub trait VerifiedObject: Object {
    fn is_correct_type<T: Object>(object: &T) -> bool;
    fn error_message() -> String;
}
<commit_msg>Update documentation for VerifiedObject<commit_after>use traits::Object;

/// Interface for safe conversions between types
///
/// This trait is required by `Object::convert_to()` function.
///
/// All built-in types like `Hash`, `RString` etc implement it.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate ruru;
///
/// use ruru::{Class, Fixnum, VM};
/// use ruru::traits::{Object, VerifiedObject};
///
/// class!(Server);
///
/// impl VerifiedObject for Server {
///     fn is_correct_type<T: Object>(object: &T) -> bool {
///         object.class() == Class::from_existing("Server")
///     }
///
///     fn error_message() -> String {
///         "Error converting to Server".to_string()
///     }
/// }
///
/// fn main() {
///     # VM::init();
///     Class::new("Server");
///
///     let server = Class::from_existing("Server").new_instance(vec![]).to_any_object();
///
///     let server_unsafe = unsafe { server.to::<Server>() };
///     let server_safe = server.try_convert_to::<Server>();
///
///     assert_eq!(server_safe, Ok(server_unsafe));
/// }
/// ```
pub trait VerifiedObject: Object {
    fn is_correct_type<T: Object>(object: &T) -> bool;
    fn error_message() -> String;
}
<|endoftext|>


<commit_before>use std::num::{One, Zero};
use std::rand::{Rand, Rng, RngUtil};
use std::cmp::ApproxEq;
use traits::division_ring::DivisionRing;
use traits::rlmul::{RMul, LMul};
use traits::dim::Dim;
use traits::inv::Inv;
use traits::transpose::Transpose;
use traits::rotation::{Rotation, Rotate, Rotatable};
use traits::transformation::{Transform}; // FIXME: implement Transformation and Transformable
use traits::homogeneous::ToHomogeneous;
use traits::indexable::Indexable;
use traits::norm::Norm;
use vec::Vec1;
use mat::{Mat2, Mat3};
use vec::Vec3;

#[deriving(Eq, ToStr)]
pub struct Rotmat<M>
{ priv submat: M }

pub fn rotmat2<N: Copy + Trigonometric + Neg<N>>(angle: N) -> Rotmat<Mat2<N>>
{
  let coa = angle.cos();
  let sia = angle.sin();

  Rotmat
  { submat: Mat2::new( [ copy coa, -sia, copy sia, copy coa ] ) }
}

pub fn rotmat3<N: Copy + Trigonometric + DivisionRing + Algebraic>
(axisangle: Vec3<N>) -> Rotmat<Mat3<N>>
{
  let mut axis  = axisangle;
  let angle     = axis.normalize();
  let _1        = One::one::<N>();
  let ux        = copy axis.at[0];
  let uy        = copy axis.at[1];
  let uz        = copy axis.at[2];
  let sqx       = ux * ux;
  let sqy       = uy * uy;
  let sqz       = uz * uz;
  let cos       = angle.cos();
  let one_m_cos = _1 - cos;
  let sin       = angle.sin();

  Rotmat {
    submat: Mat3::new( [
      (sqx + (_1 - sqx) * cos),
      (ux * uy * one_m_cos - uz * sin),
      (ux * uz * one_m_cos + uy * sin),

      (ux * uy * one_m_cos + uz * sin),
      (sqy + (_1 - sqy) * cos),
      (uy * uz * one_m_cos - ux * sin),

      (ux * uz * one_m_cos - uy * sin),
      (uy * uz * one_m_cos + ux * sin),
      (sqz + (_1 - sqz) * cos) ] )
  }
}

impl<N: Trigonometric + DivisionRing + Copy>
Rotation<Vec1<N>> for Rotmat<Mat2<N>>
{
  #[inline]
  fn rotation(&self) -> Vec1<N>
  { Vec1::new([ -(self.submat.at((0, 1)) / self.submat.at((0, 0))).atan() ]) }

  #[inline]
  fn inv_rotation(&self) -> Vec1<N>
  { -self.rotation() }

  #[inline]
  fn rotate_by(&mut self, rot: &Vec1<N>)
  { *self = self.rotated(rot) }
}

impl<N: Trigonometric + DivisionRing + Copy>
Rotatable<Vec1<N>, Rotmat<Mat2<N>>> for Rotmat<Mat2<N>>
{
  #[inline]
  fn rotated(&self, rot: &Vec1<N>) -> Rotmat<Mat2<N>>
  { rotmat2(copy rot.at[0]) * *self }
}

impl<N: Copy + Trigonometric + DivisionRing + Algebraic>
Rotation<Vec3<N>> for Rotmat<Mat3<N>>
{
  #[inline]
  fn rotation(&self) -> Vec3<N>
  { fail!("Not yet implemented.") }
  #[inline]

  fn inv_rotation(&self) -> Vec3<N>
  { fail!("Not yet implemented.") }

  #[inline]
  fn rotate_by(&mut self, rot: &Vec3<N>)
  { *self = self.rotated(rot) }
}

impl<N: Copy + Trigonometric + DivisionRing + Algebraic>
Rotatable<Vec3<N>, Rotmat<Mat3<N>>> for Rotmat<Mat3<N>>
{
  #[inline]
  fn rotated(&self, axisangle: &Vec3<N>) -> Rotmat<Mat3<N>>
  { rotmat3(copy *axisangle) * *self }
}

impl<N: Copy + Rand + Trigonometric + Neg<N>> Rand for Rotmat<Mat2<N>>
{
  #[inline]
  fn rand<R: Rng>(rng: &mut R) -> Rotmat<Mat2<N>>
  { rotmat2(rng.gen()) }
}

impl<M: RMul<V> + LMul<V>, V> Rotate<V> for Rotmat<M>
{
  #[inline]
  fn rotate(&self, v: &V) -> V
  { self.rmul(v) }

  #[inline]
  fn inv_rotate(&self, v: &V) -> V
  { self.lmul(v) }
}

impl<M: RMul<V> + LMul<V>, V> Transform<V> for Rotmat<M>
{
  #[inline]
  fn transform_vec(&self, v: &V) -> V
  { self.rotate(v) }

  #[inline]
  fn inv_transform(&self, v: &V) -> V
  { self.inv_rotate(v) }
}

impl<N: Copy + Rand + Trigonometric + DivisionRing + Algebraic>
Rand for Rotmat<Mat3<N>>
{
  #[inline]
  fn rand<R: Rng>(rng: &mut R) -> Rotmat<Mat3<N>>
  { rotmat3(rng.gen()) }
}

impl<M: Dim> Dim for Rotmat<M>
{
  #[inline]
  fn dim() -> uint
  { Dim::dim::<M>() }
}

impl<M: One + Zero> One for Rotmat<M>
{
  #[inline]
  fn one() -> Rotmat<M>
  { Rotmat { submat: One::one() } }
}

impl<M: Mul<M, M>> Mul<Rotmat<M>, Rotmat<M>> for Rotmat<M>
{
  #[inline]
  fn mul(&self, other: &Rotmat<M>) -> Rotmat<M>
  { Rotmat { submat: self.submat.mul(&other.submat) } }
}

impl<V, M: RMul<V>> RMul<V> for Rotmat<M>
{
  #[inline]
  fn rmul(&self, other: &V) -> V
  { self.submat.rmul(other) }
}

impl<V, M: LMul<V>> LMul<V> for Rotmat<M>
{
  #[inline]
  fn lmul(&self, other: &V) -> V
  { self.submat.lmul(other) }
}

impl<M: Transpose> Inv for Rotmat<M>
{
  #[inline]
  fn invert(&mut self)
  { self.transpose() }

  #[inline]
  fn inverse(&self) -> Rotmat<M>
  { self.transposed() }
}

impl<M: Transpose>
Transpose for Rotmat<M>
{
  #[inline]
  fn transposed(&self) -> Rotmat<M>
  { Rotmat { submat: self.submat.transposed() } }

  #[inline]
  fn transpose(&mut self)
  { self.submat.transpose() }
}

// we loose the info that we are a rotation matrix
impl<M: ToHomogeneous<M2>, M2> ToHomogeneous<M2> for Rotmat<M>
{
  fn to_homogeneous(&self) -> M2
  { self.submat.to_homogeneous() }
}

impl<N: ApproxEq<N>, M: ApproxEq<N>> ApproxEq<N> for Rotmat<M>
{
  #[inline]
  fn approx_epsilon() -> N
  { ApproxEq::approx_epsilon::<N, N>() }

  #[inline]
  fn approx_eq(&self, other: &Rotmat<M>) -> bool
  { self.submat.approx_eq(&other.submat) }

  #[inline]
  fn approx_eq_eps(&self, other: &Rotmat<M>, epsilon: &N) -> bool
  { self.submat.approx_eq_eps(&other.submat, epsilon) }
}
<commit_msg>Add read-only access to a Rotmat subcomponents.<commit_after>use std::num::{One, Zero};
use std::rand::{Rand, Rng, RngUtil};
use std::cmp::ApproxEq;
use traits::division_ring::DivisionRing;
use traits::rlmul::{RMul, LMul};
use traits::dim::Dim;
use traits::inv::Inv;
use traits::transpose::Transpose;
use traits::rotation::{Rotation, Rotate, Rotatable};
use traits::transformation::{Transform}; // FIXME: implement Transformation and Transformable
use traits::homogeneous::ToHomogeneous;
use traits::indexable::Indexable;
use traits::norm::Norm;
use vec::Vec1;
use mat::{Mat2, Mat3};
use vec::Vec3;

#[deriving(Eq, ToStr)]
pub struct Rotmat<M>
{ priv submat: M }

impl<M: Copy> Rotmat<M>
{
  pub fn submat(&self) -> M
  { copy self.submat }
}

pub fn rotmat2<N: Copy + Trigonometric + Neg<N>>(angle: N) -> Rotmat<Mat2<N>>
{
  let coa = angle.cos();
  let sia = angle.sin();

  Rotmat
  { submat: Mat2::new( [ copy coa, -sia, copy sia, copy coa ] ) }
}

pub fn rotmat3<N: Copy + Trigonometric + DivisionRing + Algebraic>
(axisangle: Vec3<N>) -> Rotmat<Mat3<N>>
{
  let mut axis  = axisangle;
  let angle     = axis.normalize();
  let _1        = One::one::<N>();
  let ux        = copy axis.at[0];
  let uy        = copy axis.at[1];
  let uz        = copy axis.at[2];
  let sqx       = ux * ux;
  let sqy       = uy * uy;
  let sqz       = uz * uz;
  let cos       = angle.cos();
  let one_m_cos = _1 - cos;
  let sin       = angle.sin();

  Rotmat {
    submat: Mat3::new( [
      (sqx + (_1 - sqx) * cos),
      (ux * uy * one_m_cos - uz * sin),
      (ux * uz * one_m_cos + uy * sin),

      (ux * uy * one_m_cos + uz * sin),
      (sqy + (_1 - sqy) * cos),
      (uy * uz * one_m_cos - ux * sin),

      (ux * uz * one_m_cos - uy * sin),
      (uy * uz * one_m_cos + ux * sin),
      (sqz + (_1 - sqz) * cos) ] )
  }
}

impl<N: Trigonometric + DivisionRing + Copy>
Rotation<Vec1<N>> for Rotmat<Mat2<N>>
{
  #[inline]
  fn rotation(&self) -> Vec1<N>
  { Vec1::new([ -(self.submat.at((0, 1)) / self.submat.at((0, 0))).atan() ]) }

  #[inline]
  fn inv_rotation(&self) -> Vec1<N>
  { -self.rotation() }

  #[inline]
  fn rotate_by(&mut self, rot: &Vec1<N>)
  { *self = self.rotated(rot) }
}

impl<N: Trigonometric + DivisionRing + Copy>
Rotatable<Vec1<N>, Rotmat<Mat2<N>>> for Rotmat<Mat2<N>>
{
  #[inline]
  fn rotated(&self, rot: &Vec1<N>) -> Rotmat<Mat2<N>>
  { rotmat2(copy rot.at[0]) * *self }
}

impl<N: Copy + Trigonometric + DivisionRing + Algebraic>
Rotation<Vec3<N>> for Rotmat<Mat3<N>>
{
  #[inline]
  fn rotation(&self) -> Vec3<N>
  { fail!("Not yet implemented.") }
  #[inline]

  fn inv_rotation(&self) -> Vec3<N>
  { fail!("Not yet implemented.") }

  #[inline]
  fn rotate_by(&mut self, rot: &Vec3<N>)
  { *self = self.rotated(rot) }
}

impl<N: Copy + Trigonometric + DivisionRing + Algebraic>
Rotatable<Vec3<N>, Rotmat<Mat3<N>>> for Rotmat<Mat3<N>>
{
  #[inline]
  fn rotated(&self, axisangle: &Vec3<N>) -> Rotmat<Mat3<N>>
  { rotmat3(copy *axisangle) * *self }
}

impl<N: Copy + Rand + Trigonometric + Neg<N>> Rand for Rotmat<Mat2<N>>
{
  #[inline]
  fn rand<R: Rng>(rng: &mut R) -> Rotmat<Mat2<N>>
  { rotmat2(rng.gen()) }
}

impl<M: RMul<V> + LMul<V>, V> Rotate<V> for Rotmat<M>
{
  #[inline]
  fn rotate(&self, v: &V) -> V
  { self.rmul(v) }

  #[inline]
  fn inv_rotate(&self, v: &V) -> V
  { self.lmul(v) }
}

impl<M: RMul<V> + LMul<V>, V> Transform<V> for Rotmat<M>
{
  #[inline]
  fn transform_vec(&self, v: &V) -> V
  { self.rotate(v) }

  #[inline]
  fn inv_transform(&self, v: &V) -> V
  { self.inv_rotate(v) }
}

impl<N: Copy + Rand + Trigonometric + DivisionRing + Algebraic>
Rand for Rotmat<Mat3<N>>
{
  #[inline]
  fn rand<R: Rng>(rng: &mut R) -> Rotmat<Mat3<N>>
  { rotmat3(rng.gen()) }
}

impl<M: Dim> Dim for Rotmat<M>
{
  #[inline]
  fn dim() -> uint
  { Dim::dim::<M>() }
}

impl<M: One + Zero> One for Rotmat<M>
{
  #[inline]
  fn one() -> Rotmat<M>
  { Rotmat { submat: One::one() } }
}

impl<M: Mul<M, M>> Mul<Rotmat<M>, Rotmat<M>> for Rotmat<M>
{
  #[inline]
  fn mul(&self, other: &Rotmat<M>) -> Rotmat<M>
  { Rotmat { submat: self.submat.mul(&other.submat) } }
}

impl<V, M: RMul<V>> RMul<V> for Rotmat<M>
{
  #[inline]
  fn rmul(&self, other: &V) -> V
  { self.submat.rmul(other) }
}

impl<V, M: LMul<V>> LMul<V> for Rotmat<M>
{
  #[inline]
  fn lmul(&self, other: &V) -> V
  { self.submat.lmul(other) }
}

impl<M: Transpose> Inv for Rotmat<M>
{
  #[inline]
  fn invert(&mut self)
  { self.transpose() }

  #[inline]
  fn inverse(&self) -> Rotmat<M>
  { self.transposed() }
}

impl<M: Transpose>
Transpose for Rotmat<M>
{
  #[inline]
  fn transposed(&self) -> Rotmat<M>
  { Rotmat { submat: self.submat.transposed() } }

  #[inline]
  fn transpose(&mut self)
  { self.submat.transpose() }
}

// we loose the info that we are a rotation matrix
impl<M: ToHomogeneous<M2>, M2> ToHomogeneous<M2> for Rotmat<M>
{
  fn to_homogeneous(&self) -> M2
  { self.submat.to_homogeneous() }
}

impl<N: ApproxEq<N>, M: ApproxEq<N>> ApproxEq<N> for Rotmat<M>
{
  #[inline]
  fn approx_epsilon() -> N
  { ApproxEq::approx_epsilon::<N, N>() }

  #[inline]
  fn approx_eq(&self, other: &Rotmat<M>) -> bool
  { self.submat.approx_eq(&other.submat) }

  #[inline]
  fn approx_eq_eps(&self, other: &Rotmat<M>, epsilon: &N) -> bool
  { self.submat.approx_eq_eps(&other.submat, epsilon) }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

extern crate gcc;
extern crate pkg_config;

use std::ascii::AsciiExt;
use std::process::Command;
use gcc::Config;
use std::env;
use std::path::Path;

const MIN_MAJOR: u16 = 3;
const MIN_MINOR: u16 = 4;
const MINOR_STEP: u16 = 2;

fn main() {
    let lib = pkg_config::find_library("gtk+-3.0")
        .unwrap_or_else(|e| panic!("{}", e));
    let mut parts = lib.version.splitn(3, '.')
        .map(|s| s.parse())
        .take_while(|r| r.is_ok())
        .map(|r| r.unwrap());
    let version: (u16, u16) = (parts.next().unwrap_or(0), parts.next().unwrap_or(0));
    let mut cfgs = Vec::new();
    if version.0 == MIN_MAJOR && version.1 > MIN_MINOR {
        let major = version.0;
        let mut minor = MIN_MINOR;
        while minor <= version.1 {
            cfgs.push(format!("gtk_{}_{}", major, minor));
            minor += MINOR_STEP;
        }
    }
    for cfg in &cfgs {
        println!("cargo:rustc-cfg={}", cfg);
    }
    println!("cargo:cfg={}", cfgs.join(" "));

    env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");

    // call native pkg-config, there is no way to do this with pkg-config for now
    let cmd = Command::new("pkg-config").arg("--cflags").arg("gtk+-3.0")
                .output().unwrap();
    if !cmd.status.success() {
        panic!("{}", String::from_utf8_lossy(&cmd.stderr));
    }

    // make the vector of path to set to gcc::Config
    let output = String::from_utf8(cmd.stdout).unwrap();

    // build include path
    let mut gcc_conf = Config::new();
    for s in output.split(' ') {
        if s.starts_with("-I") {
            let path: &Path = s[2..].as_ref();
            gcc_conf.include(path);
        }
    }
    gcc_conf.file("src/gtk_glue.c");

    // pass the GTK feature flags
    for cfg in &cfgs {
        gcc_conf.flag(&format!("-D{}", cfg.to_ascii_uppercase()));
    }

    // build library
    gcc_conf.compile("librgtk_glue.a");
}
<commit_msg>Parse the output of pkg-config in a more reliable way.<commit_after>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

extern crate gcc;
extern crate pkg_config;

use std::ascii::AsciiExt;
use std::process::Command;
use gcc::Config;
use std::env;
use std::path::Path;

const MIN_MAJOR: u16 = 3;
const MIN_MINOR: u16 = 4;
const MINOR_STEP: u16 = 2;

fn main() {
    let lib = pkg_config::find_library("gtk+-3.0")
        .unwrap_or_else(|e| panic!("{}", e));
    let mut parts = lib.version.splitn(3, '.')
        .map(|s| s.parse())
        .take_while(|r| r.is_ok())
        .map(|r| r.unwrap());
    let version: (u16, u16) = (parts.next().unwrap_or(0), parts.next().unwrap_or(0));
    let mut cfgs = Vec::new();
    if version.0 == MIN_MAJOR && version.1 > MIN_MINOR {
        let major = version.0;
        let mut minor = MIN_MINOR;
        while minor <= version.1 {
            cfgs.push(format!("gtk_{}_{}", major, minor));
            minor += MINOR_STEP;
        }
    }
    for cfg in &cfgs {
        println!("cargo:rustc-cfg={}", cfg);
    }
    println!("cargo:cfg={}", cfgs.join(" "));

    env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");

    // call native pkg-config, there is no way to do this with pkg-config for now
    let cmd = Command::new("pkg-config").arg("--cflags").arg("gtk+-3.0")
                .output().unwrap();
    if !cmd.status.success() {
        panic!("{}", String::from_utf8_lossy(&cmd.stderr));
    }

    // make the vector of path to set to gcc::Config
    let output = String::from_utf8(cmd.stdout).unwrap();

    // build include path
    let mut gcc_conf = Config::new();
    for s in output.split_whitespace() {
        if s.starts_with("-I") {
            let path: &Path = s[2..].as_ref();
            gcc_conf.include(path);
        }
    }
    gcc_conf.file("src/gtk_glue.c");

    // pass the GTK feature flags
    for cfg in &cfgs {
        gcc_conf.flag(&format!("-D{}", cfg.to_ascii_uppercase()));
    }

    // build library
    gcc_conf.compile("librgtk_glue.a");
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventSourceBinding::{EventSourceInit, EventSourceMethods, Wrap};
use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::reflect_dom_object;
use dom::eventtarget::EventTarget;
use std::cell::Cell;
use url::Url;
use util::str::DOMString;

#[derive(JSTraceable, PartialEq, Copy, Clone, Debug, HeapSizeOf)]
enum EventSourceReadyState {
    Connecting = 0,
    Open = 1,
    Closed = 2
}

#[dom_struct]
pub struct EventSource {
    eventtarget: EventTarget,
    url: Url,
    ready_state: Cell<EventSourceReadyState>,
    with_credentials: bool,
    last_event_id: DOMRefCell<DOMString>
}

impl EventSource {
    fn new_inherited(url: Url, with_credentials: bool) -> EventSource {
        EventSource {
            eventtarget: EventTarget::new_inherited(),
            url: url,
            ready_state: Cell::new(EventSourceReadyState::Connecting),
            with_credentials: with_credentials,
            last_event_id: DOMRefCell::new(DOMString::from(""))
        }
    }

    fn new(global: GlobalRef, url: Url, with_credentials: bool) -> Root<EventSource> {
        reflect_dom_object(box EventSource::new_inherited(url, with_credentials), global, Wrap)
    }

    pub fn Constructor(global: GlobalRef,
                       url_str: DOMString,
                       event_source_init: &EventSourceInit) -> Fallible<Root<EventSource>> {
        // Steps 1-2
        let base_url = global.get_url();
        let url = match base_url.join(&*url_str) {
            Ok(u) => u,
            Err(_) => return Err(Error::Syntax)
        };
        // Step 3
        let event_source = EventSource::new(global, url, event_source_init.withCredentials);
        // Step 4
        // Step 5
        // Step 6
        // Step 7
        // Step 8
        // Step 9
        // Step 10
        // Step 11
        Ok(event_source)
        // Step 12
    }
}

impl EventSourceMethods for EventSource {
    // https://html.spec.whatwg.org/multipage/#handler-eventsource-onopen
    event_handler!(open, GetOnopen, SetOnopen);

    // https://html.spec.whatwg.org/multipage/#handler-eventsource-onmessage
    event_handler!(message, GetOnmessage, SetOnmessage);

    // https://html.spec.whatwg.org/multipage/#handler-eventsource-onerror
    event_handler!(error, GetOnerror, SetOnerror);

    // https://html.spec.whatwg.org/multipage/#dom-eventsource-url
    fn Url(&self) -> DOMString {
        DOMString::from(self.url.serialize())
    }

    // https://html.spec.whatwg.org/multipage/#dom-eventsource-withcredentials
    fn WithCredentials(&self) -> bool {
        self.with_credentials
    }

    // https://html.spec.whatwg.org/multipage/#dom-eventsource-readystate
    fn ReadyState(&self) -> u16 {
        self.ready_state.get() as u16
    }

    // https://html.spec.whatwg.org/multipage/#dom-eventsource-close
    fn Close(&self) {
        self.ready_state.set(EventSourceReadyState::Closed);
        // TODO: Terminate ongoing fetch
    }
}
<commit_msg>Auto merge of #9182 - Ms2ger:es-warning, r=Manishearth<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventSourceBinding::{EventSourceInit, EventSourceMethods, Wrap};
use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::reflect_dom_object;
use dom::eventtarget::EventTarget;
use std::cell::Cell;
use url::Url;
use util::str::DOMString;

#[derive(JSTraceable, PartialEq, Copy, Clone, Debug, HeapSizeOf)]
enum EventSourceReadyState {
    Connecting = 0,
    #[allow(dead_code)]
    Open = 1,
    Closed = 2
}

#[dom_struct]
pub struct EventSource {
    eventtarget: EventTarget,
    url: Url,
    ready_state: Cell<EventSourceReadyState>,
    with_credentials: bool,
    last_event_id: DOMRefCell<DOMString>
}

impl EventSource {
    fn new_inherited(url: Url, with_credentials: bool) -> EventSource {
        EventSource {
            eventtarget: EventTarget::new_inherited(),
            url: url,
            ready_state: Cell::new(EventSourceReadyState::Connecting),
            with_credentials: with_credentials,
            last_event_id: DOMRefCell::new(DOMString::from(""))
        }
    }

    fn new(global: GlobalRef, url: Url, with_credentials: bool) -> Root<EventSource> {
        reflect_dom_object(box EventSource::new_inherited(url, with_credentials), global, Wrap)
    }

    pub fn Constructor(global: GlobalRef,
                       url_str: DOMString,
                       event_source_init: &EventSourceInit) -> Fallible<Root<EventSource>> {
        // Steps 1-2
        let base_url = global.get_url();
        let url = match base_url.join(&*url_str) {
            Ok(u) => u,
            Err(_) => return Err(Error::Syntax)
        };
        // Step 3
        let event_source = EventSource::new(global, url, event_source_init.withCredentials);
        // Step 4
        // Step 5
        // Step 6
        // Step 7
        // Step 8
        // Step 9
        // Step 10
        // Step 11
        Ok(event_source)
        // Step 12
    }
}

impl EventSourceMethods for EventSource {
    // https://html.spec.whatwg.org/multipage/#handler-eventsource-onopen
    event_handler!(open, GetOnopen, SetOnopen);

    // https://html.spec.whatwg.org/multipage/#handler-eventsource-onmessage
    event_handler!(message, GetOnmessage, SetOnmessage);

    // https://html.spec.whatwg.org/multipage/#handler-eventsource-onerror
    event_handler!(error, GetOnerror, SetOnerror);

    // https://html.spec.whatwg.org/multipage/#dom-eventsource-url
    fn Url(&self) -> DOMString {
        DOMString::from(self.url.serialize())
    }

    // https://html.spec.whatwg.org/multipage/#dom-eventsource-withcredentials
    fn WithCredentials(&self) -> bool {
        self.with_credentials
    }

    // https://html.spec.whatwg.org/multipage/#dom-eventsource-readystate
    fn ReadyState(&self) -> u16 {
        self.ready_state.get() as u16
    }

    // https://html.spec.whatwg.org/multipage/#dom-eventsource-close
    fn Close(&self) {
        self.ready_state.set(EventSourceReadyState::Closed);
        // TODO: Terminate ongoing fetch
    }
}
<|endoftext|>


<commit_before>use num::{Complex, Zero};
use common::FFTnum;

use algorithm::FFTAlgorithm;
use twiddles;

pub struct DFTAlgorithm<T> {
    twiddles: Vec<Complex<T>>,
}

impl<T: FFTnum> DFTAlgorithm<T> {
    pub fn new(len: usize, inverse: bool) -> Self {
        Self {
            twiddles: twiddles::generate_twiddle_factors(len, inverse),
        }
    }

    fn perform_fft(&self, signal: &[Complex<T>], spectrum: &mut [Complex<T>]) {
        for (k, output_cell) in spectrum.iter_mut().enumerate() {
            let mut sum = Zero::zero();
            for (i, &input_cell) in signal.iter().enumerate() {
                let twiddle = unsafe { *self.twiddles.get_unchecked((k * i) % self.twiddles.len()) };
                sum = sum + twiddle * input_cell;
            }
            *output_cell = sum;
        }
    }
}

impl<T: FFTnum> FFTAlgorithm<T> for DFTAlgorithm<T> {
    fn process(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        self.perform_fft(input, output);
    }
    fn process_multi(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        for (in_chunk, out_chunk) in input.chunks_mut(self.len()).zip(output.chunks_mut(self.len())) {
            self.perform_fft(in_chunk, out_chunk);
        }
    }
    fn len(&self) -> usize {
        self.twiddles.len()
    }
}

#[cfg(test)]
mod unit_tests {
    use super::*;
    use test_utils::{random_signal, compare_vectors};
    use dft;

    #[test]
    fn test_matches_dft() {
        for len in 1..50 {
            let mut input = random_signal(len);
            let mut expected = input.clone();
            dft(&input, &mut expected);

            let mut actual = input.clone();
            let wrapper = DFTAlgorithm::new(len, false);
            wrapper.process(&mut input, &mut actual);

            assert!(compare_vectors(&expected, &actual), "length = {}", len);
        }
    }
}<commit_msg>Performance improvements to DFT implementation<commit_after>use num::{Complex, Zero};
use common::FFTnum;

use algorithm::FFTAlgorithm;
use twiddles;

pub struct DFTAlgorithm<T> {
    twiddles: Vec<Complex<T>>,
}

impl<T: FFTnum> DFTAlgorithm<T> {
    pub fn new(len: usize, inverse: bool) -> Self {
        Self {
            twiddles: twiddles::generate_twiddle_factors(len, inverse),
        }
    }

    #[inline(always)]
    fn perform_fft(&self, signal: &[Complex<T>], spectrum: &mut [Complex<T>]) {
        for k in 0..spectrum.len() {
            let output_cell = unsafe { spectrum.get_unchecked_mut(k) };

            *output_cell = Zero::zero();
            let mut twiddle_index = 0;

            for input_cell in signal {
                let twiddle = unsafe { *self.twiddles.get_unchecked(twiddle_index) };
                *output_cell = *output_cell + twiddle * input_cell;

                twiddle_index += k;
                if twiddle_index >= self.twiddles.len() {
                    twiddle_index -= self.twiddles.len();
                }
            }
        }
    }
}

impl<T: FFTnum> FFTAlgorithm<T> for DFTAlgorithm<T> {
    fn process(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        self.perform_fft(input, output);
    }
    fn process_multi(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        for (in_chunk, out_chunk) in input.chunks_mut(self.len()).zip(output.chunks_mut(self.len())) {
            self.perform_fft(in_chunk, out_chunk);
        }
    }
    fn len(&self) -> usize {
        self.twiddles.len()
    }
}

#[cfg(test)]
mod unit_tests {
    use super::*;
    use test_utils::{random_signal, compare_vectors};
    use dft;

    #[test]
    fn test_matches_dft() {
        for len in 1..50 {
            let mut input = random_signal(len);
            let mut expected = input.clone();
            dft(&input, &mut expected);

            let mut actual = input.clone();
            let wrapper = DFTAlgorithm::new(len, false);
            wrapper.process(&mut input, &mut actual);

            assert!(compare_vectors(&expected, &actual), "length = {}", len);
        }
    }
}<|endoftext|>