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

use admission_control_service::admission_control_service::AdmissionControlService;
use consensus::consensus_provider::{make_consensus_provider, ConsensusProvider};
use debug_interface::{
    node_debug_service::NodeDebugService,
    proto::node_debug_interface_server::NodeDebugInterfaceServer,
};
use executor::Executor;
use futures::{channel::mpsc::channel, executor::block_on};
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()
        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::net::ToSocketAddrs;
use std::{sync::Arc, thread, time::Instant};
use storage_client::{StorageReadServiceClient, StorageWriteServiceClient};
use storage_service::start_storage_service;
use tokio::runtime::{Builder, Runtime};
use vm_runtime::LibraVM;

const AC_SMP_CHANNEL_BUFFER_SIZE: usize = 1_024;

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

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 storage_read_client = Arc::new(StorageReadServiceClient::new(
        &config.storage.address,
        config.storage.port,
    ));
    let storage_write_client = Arc::new(StorageWriteServiceClient::new(
        &config.storage.address,
        config.storage.port,
    ));

    Arc::new(Executor::new(
        storage_read_client,
        storage_write_client,
        config,
    ))
}

fn setup_debug_interface(config: &NodeConfig) -> Runtime {
    let rt = tokio::runtime::Runtime::new().unwrap();
    let addr = format!(
        "{}:{}",
        config.debug_interface.address, config.debug_interface.admission_control_node_debug_port,
    )
    .to_socket_addrs()
    .unwrap()
    .next()
    .unwrap();
    rt.spawn(
        tonic::transport::Server::builder()
            .add_service(NodeDebugInterfaceServer::new(NodeDebugService::new()))
            .serve(addr),
    );
    rt
}

// 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)]);
    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 mempool_network_handles = 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 (mempool_sender, mempool_events) = network_provider
            .add_mempool(vec![ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL)]);
        mempool_network_handles.push((network.peer_id, mempool_sender, mempool_events));

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

    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 (mempool_sender, mempool_events) = network_provider
            .add_mempool(vec![ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL)]);
        mempool_network_handles.push((
            node_config.full_node_networks[i].peer_id,
            mempool_sender,
            mempool_events,
        ));

        let network = &node_config.full_node_networks[i];
        // 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 = 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 (ac_sender, client_events) = channel(AC_SMP_CHANNEL_BUFFER_SIZE);
    let admission_control_runtime = AdmissionControlService::bootstrap(&node_config, ac_sender);

    instant = Instant::now();
    let mempool = Some(MempoolRuntime::bootstrap(
        node_config,
        mempool_network_handles,
        client_events,
    ));
    debug!("Mempool started in {} ms", instant.elapsed().as_millis());
    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 (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 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_runtime,
        _mempool: mempool,
        _state_synchronizer: state_synchronizer,
        consensus,
        _storage: storage,
        _debug: debug_if,
    }
}
<commit_msg>[mempool] make mempool runtime required in LibraHandle<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use admission_control_service::admission_control_service::AdmissionControlService;
use consensus::consensus_provider::{make_consensus_provider, ConsensusProvider};
use debug_interface::{
    node_debug_service::NodeDebugService,
    proto::node_debug_interface_server::NodeDebugInterfaceServer,
};
use executor::Executor;
use futures::{channel::mpsc::channel, executor::block_on};
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()
        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::net::ToSocketAddrs;
use std::{sync::Arc, thread, time::Instant};
use storage_client::{StorageReadServiceClient, StorageWriteServiceClient};
use storage_service::start_storage_service;
use tokio::runtime::{Builder, Runtime};
use vm_runtime::LibraVM;

const AC_SMP_CHANNEL_BUFFER_SIZE: usize = 1_024;

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

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 storage_read_client = Arc::new(StorageReadServiceClient::new(
        &config.storage.address,
        config.storage.port,
    ));
    let storage_write_client = Arc::new(StorageWriteServiceClient::new(
        &config.storage.address,
        config.storage.port,
    ));

    Arc::new(Executor::new(
        storage_read_client,
        storage_write_client,
        config,
    ))
}

fn setup_debug_interface(config: &NodeConfig) -> Runtime {
    let rt = tokio::runtime::Runtime::new().unwrap();
    let addr = format!(
        "{}:{}",
        config.debug_interface.address, config.debug_interface.admission_control_node_debug_port,
    )
    .to_socket_addrs()
    .unwrap()
    .next()
    .unwrap();
    rt.spawn(
        tonic::transport::Server::builder()
            .add_service(NodeDebugInterfaceServer::new(NodeDebugService::new()))
            .serve(addr),
    );
    rt
}

// 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)]);
    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 mempool_network_handles = 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 (mempool_sender, mempool_events) = network_provider
            .add_mempool(vec![ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL)]);
        mempool_network_handles.push((network.peer_id, mempool_sender, mempool_events));

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

    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 (mempool_sender, mempool_events) = network_provider
            .add_mempool(vec![ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL)]);
        mempool_network_handles.push((
            node_config.full_node_networks[i].peer_id,
            mempool_sender,
            mempool_events,
        ));

        let network = &node_config.full_node_networks[i];
        // 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 = 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 (ac_sender, client_events) = channel(AC_SMP_CHANNEL_BUFFER_SIZE);
    let admission_control_runtime = AdmissionControlService::bootstrap(&node_config, ac_sender);

    instant = Instant::now();
    let mempool = MempoolRuntime::bootstrap(node_config, mempool_network_handles, client_events);
    debug!("Mempool started in {} ms", instant.elapsed().as_millis());
    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 (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 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_runtime,
        _mempool: mempool,
        _state_synchronizer: state_synchronizer,
        consensus,
        _storage: storage,
        _debug: debug_if,
    }
}
<|endoftext|>


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

use anyhow::Result;

use crate::{
    analysis::{
        cfg::CFG,
        pe::{self, ImportedSymbol},
    },
    loader::pe::PE,
};

pub fn cfg_prune_noret_imports(pe: &PE, cfg: &mut CFG) -> Result<()> {
    let noret = pe::get_imports(&pe)?
        .values()
        .filter(|imp| match (&*imp.dll, &imp.symbol) {
            ("kernel32.dll", ImportedSymbol::Name(symbol)) if symbol == "ExitProcess" => true,
            ("kernel32.dll", ImportedSymbol::Ordinal(171)) => true,
            (_, _) => false,
        })
        .map(|imp| imp.address)
        .collect::<BTreeSet<_>>();

    for &noret_import in noret.iter() {
        log::info!("noret import {:#x}", noret_import);
        crate::analysis::cfg::noret::cfg_mark_noret(&pe.module, cfg, noret_import)?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use std::ops::Not;

    use super::*;
    use crate::{
        analysis::cfg::{InstructionIndex, CFG},
        rsrc::*,
    };
    use anyhow::Result;

    #[test]
    fn nop() -> Result<()> {
        let buf = get_buf(Rsrc::NOP);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;
        let mut insns: InstructionIndex = Default::default();

        for &ep in crate::analysis::pe::entrypoints::find_pe_entrypoint(&pe)?.iter() {
            insns.build_index(&pe.module, ep)?;
        }

        for &exp in crate::analysis::pe::exports::find_pe_exports(&pe)?.iter() {
            insns.build_index(&pe.module, exp)?;
        }

        let mut cfg = CFG::from_instructions(&pe.module, insns)?;

        cfg_prune_noret_imports(&pe, &mut cfg)?;

        // there are two calls to ExitProcess:
        //
        //     .text:00401C73                 push    [esp+uExitCode] ; uExitCode
        //     .text:00401C77                 call    ds:ExitProcess
        //     .text:00401C77 ___crtExitProcess endp
        //     .text:00401C77
        //     .text:00401C77 ;
        // ---------------------------------------------------------------------------
        //     .text:00401C7D                 align 2

        assert!(cfg.insns.insns_by_address.contains_key(&0x401C77));
        assert!(cfg.insns.insns_by_address.contains_key(&0x401C7D).not());

        // and:
        //
        //     .text:00402D69                 push    3               ; uExitCode
        //     .text:00402D6B                 call    ds:ExitProcess
        //     .text:00402D6B ; } // starts at 402D41
        //     .text:00402D6B _report_failure endp
        //     .text:00402D6B
        //     .text:00402D6B ;
        // ---------------------------------------------------------------------------
        //     .text:00402D71                 align 2

        assert!(cfg.insns.insns_by_address.contains_key(&0x402D6B));
        assert!(cfg.insns.insns_by_address.contains_key(&0x402D71).not());

        // _report_failure is part of __security_check_cookie (tail call),
        // which is not noret.
        //
        // ___crtExitProcess is noret:
        //
        //    .text:00401C4E ; =============== S U B R O U T I N E
        // =======================================    .text:00401C4E
        //    .text:00401C4E ; Attributes: library function noreturn
        //    .text:00401C4E
        //    .text:00401C4E ; void __cdecl __noreturn __crtExitProcess(UINT uExitCode)
        //    .text:00401C4E ___crtExitProcess proc near             ; CODE XREF:
        // start+EA↑p    .text:00401C4E
        // ; _doexit+BA↓p
        //
        // which has a call to it like:
        //
        //     .text:00401161                 call    __NMSG_WRITE
        //     .text:00401166                 push    0FFh            ; uExitCode
        //     .text:0040116B                 call    ___crtExitProcess
        //     .text:00401170 ;
        // ---------------------------------------------------------------------------
        //     .text:00401170                 db  59h ; Y
        //     .text:00401171                 db  59h ; Y

        assert!(cfg.insns.insns_by_address.contains_key(&0x40116B));
        assert!(cfg.insns.insns_by_address.contains_key(&0x401170).not());

        Ok(())
    }
}
<commit_msg>pe: noret imports: return found noret functions<commit_after>use std::collections::BTreeSet;

use anyhow::Result;

use crate::{
    analysis::{
        cfg::CFG,
        pe::{self, ImportedSymbol},
    },
    loader::pe::PE,
    VA,
};

pub fn cfg_prune_noret_imports(pe: &PE, cfg: &mut CFG) -> Result<BTreeSet<VA>> {
    let mut noret = pe::get_imports(&pe)?
        .values()
        .filter(|imp| match (&*imp.dll, &imp.symbol) {
            ("kernel32.dll", ImportedSymbol::Name(symbol)) if symbol == "ExitProcess" => true,
            ("kernel32.dll", ImportedSymbol::Ordinal(171)) => true,
            (_, _) => false,
        })
        .map(|imp| imp.address)
        .collect::<BTreeSet<_>>();

    for &noret_import in noret.clone().iter() {
        log::info!("noret import {:#x}", noret_import);
        noret.extend(crate::analysis::cfg::noret::cfg_mark_noret(
            &pe.module,
            cfg,
            noret_import,
        )?);
    }

    Ok(noret)
}

#[cfg(test)]
mod tests {
    use std::ops::Not;

    use super::*;
    use crate::{
        analysis::cfg::{InstructionIndex, CFG},
        rsrc::*,
    };
    use anyhow::Result;

    #[test]
    fn nop() -> Result<()> {
        let buf = get_buf(Rsrc::NOP);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;
        let mut insns: InstructionIndex = Default::default();

        for &ep in crate::analysis::pe::entrypoints::find_pe_entrypoint(&pe)?.iter() {
            insns.build_index(&pe.module, ep)?;
        }

        for &exp in crate::analysis::pe::exports::find_pe_exports(&pe)?.iter() {
            insns.build_index(&pe.module, exp)?;
        }

        let mut cfg = CFG::from_instructions(&pe.module, insns)?;

        cfg_prune_noret_imports(&pe, &mut cfg)?;

        // there are two calls to ExitProcess:
        //
        //     .text:00401C73                 push    [esp+uExitCode] ; uExitCode
        //     .text:00401C77                 call    ds:ExitProcess
        //     .text:00401C77 ___crtExitProcess endp
        //     .text:00401C77
        //     .text:00401C77 ;
        // ---------------------------------------------------------------------------
        //     .text:00401C7D                 align 2

        assert!(cfg.insns.insns_by_address.contains_key(&0x401C77));
        assert!(cfg.insns.insns_by_address.contains_key(&0x401C7D).not());

        // and:
        //
        //     .text:00402D69                 push    3               ; uExitCode
        //     .text:00402D6B                 call    ds:ExitProcess
        //     .text:00402D6B ; } // starts at 402D41
        //     .text:00402D6B _report_failure endp
        //     .text:00402D6B
        //     .text:00402D6B ;
        // ---------------------------------------------------------------------------
        //     .text:00402D71                 align 2

        assert!(cfg.insns.insns_by_address.contains_key(&0x402D6B));
        assert!(cfg.insns.insns_by_address.contains_key(&0x402D71).not());

        // _report_failure is part of __security_check_cookie (tail call),
        // which is not noret.
        //
        // ___crtExitProcess is noret:
        //
        //    .text:00401C4E ; =============== S U B R O U T I N E
        // =======================================    .text:00401C4E
        //    .text:00401C4E ; Attributes: library function noreturn
        //    .text:00401C4E
        //    .text:00401C4E ; void __cdecl __noreturn __crtExitProcess(UINT uExitCode)
        //    .text:00401C4E ___crtExitProcess proc near             ; CODE XREF:
        // start+EA↑p    .text:00401C4E
        // ; _doexit+BA↓p
        //
        // which has a call to it like:
        //
        //     .text:00401161                 call    __NMSG_WRITE
        //     .text:00401166                 push    0FFh            ; uExitCode
        //     .text:0040116B                 call    ___crtExitProcess
        //     .text:00401170 ;
        // ---------------------------------------------------------------------------
        //     .text:00401170                 db  59h ; Y
        //     .text:00401171                 db  59h ; Y

        assert!(cfg.insns.insns_by_address.contains_key(&0x40116B));
        assert!(cfg.insns.insns_by_address.contains_key(&0x401170).not());

        Ok(())
    }
}
<|endoftext|>


<commit_before>pub(crate) mod cursor;
pub mod raw;
#[doc(hidden)]
pub mod result;
mod row;
mod stmt;

use std::ffi::CString;
use std::os::raw as libc;

use self::cursor::*;
use self::raw::{PgTransactionStatus, RawConnection};
use self::result::PgResult;
use self::stmt::Statement;
use crate::connection::commit_error_processor::{CommitErrorOutcome, CommitErrorProcessor};
use crate::connection::*;
use crate::expression::QueryMetadata;
use crate::pg::metadata_lookup::{GetPgMetadataCache, PgMetadataCache};
use crate::pg::{Pg, TransactionBuilder};
use crate::query_builder::bind_collector::RawBytesBindCollector;
use crate::query_builder::*;
use crate::result::ConnectionError::CouldntSetupConfiguration;
use crate::result::*;

/// The connection string expected by `PgConnection::establish`
/// should be a PostgreSQL connection string, as documented at
/// <https://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNSTRING>
#[allow(missing_debug_implementations)]
pub struct PgConnection {
    pub(crate) raw_connection: RawConnection,
    transaction_state: AnsiTransactionManager,
    statement_cache: StatementCache<Pg, Statement>,
    metadata_cache: PgMetadataCache,
}

unsafe impl Send for PgConnection {}

impl SimpleConnection for PgConnection {
    fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
        let query = CString::new(query)?;
        let inner_result = unsafe { self.raw_connection.exec(query.as_ptr()) };
        PgResult::new(inner_result?)?;
        Ok(())
    }
}

impl<'conn, 'query> ConnectionGatWorkaround<'conn, 'query, Pg> for PgConnection {
    type Cursor = Cursor;
    type Row = self::row::PgRow;
}

impl CommitErrorProcessor for PgConnection {
    fn process_commit_error(&self, transaction_depth: i32, error: Error) -> CommitErrorOutcome {
        if matches!(
            error,
            Error::DatabaseError(DatabaseErrorKind::ClosedConnection, _)
        ) {
            return CommitErrorOutcome::Throw(error);
        }
        if transaction_depth <= 1 {
            match error {
                Error::DatabaseError(DatabaseErrorKind::SerializationFailure, _)
                | Error::DatabaseError(DatabaseErrorKind::ReadOnlyTransaction, _) => {
                    CommitErrorOutcome::RollbackAndThrow(error)
                }
                _ => CommitErrorOutcome::Throw(error),
            }
        } else {
            let transaction_status = self.raw_connection.transaction_status();
            match error {
                Error::DatabaseError(DatabaseErrorKind::Unknown, _)
                    if transaction_status == PgTransactionStatus::InError =>
                {
                    CommitErrorOutcome::RollbackAndThrow(error)
                }
                _ => CommitErrorOutcome::Throw(error),
            }
        }
    }
}

impl Connection for PgConnection {
    type Backend = Pg;
    type TransactionManager = AnsiTransactionManager;

    fn establish(database_url: &str) -> ConnectionResult<PgConnection> {
        RawConnection::establish(database_url).and_then(|raw_conn| {
            let mut conn = PgConnection {
                raw_connection: raw_conn,
                transaction_state: AnsiTransactionManager::default(),
                statement_cache: StatementCache::new(),
                metadata_cache: PgMetadataCache::new(),
            };
            conn.set_config_options()
                .map_err(CouldntSetupConfiguration)?;
            Ok(conn)
        })
    }

    #[doc(hidden)]
    fn execute(&mut self, query: &str) -> QueryResult<usize> {
        self.execute_inner(query).map(|res| res.rows_affected())
    }

    #[doc(hidden)]
    fn load<'conn, 'query, T>(
        &'conn mut self,
        source: T,
    ) -> QueryResult<<Self as ConnectionGatWorkaround<'conn, 'query, Pg>>::Cursor>
    where
        T: AsQuery,
        T::Query: QueryFragment<Self::Backend> + QueryId + 'query,
        Self::Backend: QueryMetadata<T::SqlType>,
    {
        self.with_prepared_query(&source.as_query(), |stmt, params, conn| {
            let result = stmt.execute(conn, &params)?;
            let cursor = Cursor::new(result);

            Ok(cursor)
        })
    }

    #[doc(hidden)]
    fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
    where
        T: QueryFragment<Pg> + QueryId,
    {
        self.with_prepared_query(source, |query, params, conn| {
            query.execute(conn, &params).map(|r| r.rows_affected())
        })
    }

    fn transaction_state(&mut self) -> &mut AnsiTransactionManager
    where
        Self: Sized,
    {
        &mut self.transaction_state
    }
}

impl GetPgMetadataCache for PgConnection {
    fn get_metadata_cache(&mut self) -> &mut PgMetadataCache {
        &mut self.metadata_cache
    }
}

impl PgConnection {
    /// Build a transaction, specifying additional details such as isolation level
    ///
    /// See [`TransactionBuilder`] for more examples.
    ///
    /// [`TransactionBuilder`]: crate::pg::TransactionBuilder
    ///
    /// ```rust
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # fn main() {
    /// #     run_test().unwrap();
    /// # }
    /// #
    /// # fn run_test() -> QueryResult<()> {
    /// #     use schema::users::dsl::*;
    /// #     let conn = &mut connection_no_transaction();
    /// conn.build_transaction()
    ///     .read_only()
    ///     .serializable()
    ///     .deferrable()
    ///     .run(|conn| Ok(()))
    /// # }
    /// ```
    pub fn build_transaction(&mut self) -> TransactionBuilder<'_, Self> {
        TransactionBuilder::new(self)
    }

    fn with_prepared_query<'conn, T: QueryFragment<Pg> + QueryId, R>(
        &'conn mut self,
        source: &'_ T,
        f: impl FnOnce(
            MaybeCached<'_, Statement>,
            Vec<Option<Vec<u8>>>,
            &'conn mut RawConnection,
        ) -> QueryResult<R>,
    ) -> QueryResult<R> {
        let mut bind_collector = RawBytesBindCollector::<Pg>::new();
        source.collect_binds(&mut bind_collector, self)?;
        let binds = bind_collector.binds;
        let metadata = bind_collector.metadata;

        let cache_len = self.statement_cache.len();
        let cache = &mut self.statement_cache;
        let raw_conn = &mut self.raw_connection;
        let query = cache.cached_statement(source, &metadata, |sql, _| {
            let query_name = if source.is_safe_to_cache_prepared()? {
                Some(format!("__diesel_stmt_{}", cache_len))
            } else {
                None
            };
            Statement::prepare(raw_conn, sql, query_name.as_deref(), &metadata)
        });

        f(query?, binds, raw_conn)
    }

    fn execute_inner(&mut self, query: &str) -> QueryResult<PgResult> {
        let query = Statement::prepare(&mut self.raw_connection, query, None, &[])?;
        query.execute(&mut self.raw_connection, &Vec::new())
    }

    fn set_config_options(&mut self) -> QueryResult<()> {
        self.execute("SET TIME ZONE 'UTC'")?;
        self.execute("SET CLIENT_ENCODING TO 'UTF8'")?;
        self.raw_connection
            .set_notice_processor(noop_notice_processor);
        Ok(())
    }
}

extern "C" fn noop_notice_processor(_: *mut libc::c_void, _message: *const libc::c_char) {}

#[cfg(test)]
mod tests {
    extern crate dotenv;

    use super::*;
    use crate::dsl::sql;
    use crate::prelude::*;
    use crate::result::Error::DatabaseError;
    use crate::sql_types::{Integer, VarChar};

    #[test]
    fn malformed_sql_query() {
        let connection = &mut connection();
        let query =
            crate::sql_query("SELECT not_existent FROM also_not_there;").execute(connection);

        if let Err(err) = query {
            if let DatabaseError(_, string) = err {
                assert_eq!(Some(26), string.statement_position());
            } else {
                unreachable!();
            }
        } else {
            unreachable!();
        }
    }

    #[test]
    fn prepared_statements_are_cached() {
        let connection = &mut connection();

        let query = crate::select(1.into_sql::<Integer>());

        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(1, connection.statement_cache.len());
    }

    #[test]
    fn queries_with_identical_sql_but_different_types_are_cached_separately() {
        let connection = &mut connection();

        let query = crate::select(1.into_sql::<Integer>());
        let query2 = crate::select("hi".into_sql::<VarChar>());

        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(Ok("hi".to_string()), query2.get_result(connection));
        assert_eq!(2, connection.statement_cache.len());
    }

    #[test]
    fn queries_with_identical_types_and_sql_but_different_bind_types_are_cached_separately() {
        let connection = &mut connection();

        let query = crate::select(1.into_sql::<Integer>()).into_boxed::<Pg>();
        let query2 = crate::select("hi".into_sql::<VarChar>()).into_boxed::<Pg>();

        assert_eq!(0, connection.statement_cache.len());
        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(Ok("hi".to_string()), query2.get_result(connection));
        assert_eq!(2, connection.statement_cache.len());
    }

    sql_function!(fn lower(x: VarChar) -> VarChar);

    #[test]
    fn queries_with_identical_types_and_binds_but_different_sql_are_cached_separately() {
        let connection = &mut connection();

        let hi = "HI".into_sql::<VarChar>();
        let query = crate::select(hi).into_boxed::<Pg>();
        let query2 = crate::select(lower(hi)).into_boxed::<Pg>();

        assert_eq!(0, connection.statement_cache.len());
        assert_eq!(Ok("HI".to_string()), query.get_result(connection));
        assert_eq!(Ok("hi".to_string()), query2.get_result(connection));
        assert_eq!(2, connection.statement_cache.len());
    }

    #[test]
    fn queries_with_sql_literal_nodes_are_not_cached() {
        let connection = &mut connection();
        let query = crate::select(sql::<Integer>("1"));

        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(0, connection.statement_cache.len());
    }

    table! {
        users {
            id -> Integer,
            name -> Text,
        }
    }

    #[test]
    fn inserts_from_select_are_cached() {
        let connection = &mut connection();
        connection.begin_test_transaction().unwrap();

        crate::sql_query(
            "CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
        )
        .execute(connection)
        .unwrap();

        let query = users::table.filter(users::id.eq(42));
        let insert = query
            .insert_into(users::table)
            .into_columns((users::id, users::name));
        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(1, connection.statement_cache.len());

        let query = users::table.filter(users::id.eq(42)).into_boxed();
        let insert = query
            .insert_into(users::table)
            .into_columns((users::id, users::name));
        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(2, connection.statement_cache.len());
    }

    #[test]
    fn single_inserts_are_cached() {
        let connection = &mut connection();
        connection.begin_test_transaction().unwrap();

        crate::sql_query(
            "CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
        )
        .execute(connection)
        .unwrap();

        let insert =
            crate::insert_into(users::table).values((users::id.eq(42), users::name.eq("Foo")));

        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(1, connection.statement_cache.len());
    }

    #[test]
    fn dynamic_batch_inserts_are_not_cached() {
        let connection = &mut connection();
        connection.begin_test_transaction().unwrap();

        crate::sql_query(
            "CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
        )
        .execute(connection)
        .unwrap();

        let insert = crate::insert_into(users::table)
            .values(vec![(users::id.eq(42), users::name.eq("Foo"))]);

        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(0, connection.statement_cache.len());
    }

    #[test]
    fn static_batch_inserts_are_cached() {
        let connection = &mut connection();
        connection.begin_test_transaction().unwrap();

        crate::sql_query(
            "CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
        )
        .execute(connection)
        .unwrap();

        let insert =
            crate::insert_into(users::table).values([(users::id.eq(42), users::name.eq("Foo"))]);

        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(1, connection.statement_cache.len());
    }

    #[test]
    fn queries_containing_in_with_vec_are_cached() {
        let connection = &mut connection();
        let one_as_expr = 1.into_sql::<Integer>();
        let query = crate::select(one_as_expr.eq_any(vec![1, 2, 3]));

        assert_eq!(Ok(true), query.get_result(connection));
        assert_eq!(1, connection.statement_cache.len());
    }

    fn connection() -> PgConnection {
        crate::test_helpers::pg_connection_no_transaction()
    }
}
<commit_msg>No catch-all clause when matching errors in PG's error processor.<commit_after>pub(crate) mod cursor;
pub mod raw;
#[doc(hidden)]
pub mod result;
mod row;
mod stmt;

use std::ffi::CString;
use std::os::raw as libc;

use self::cursor::*;
use self::raw::{PgTransactionStatus, RawConnection};
use self::result::PgResult;
use self::stmt::Statement;
use crate::connection::commit_error_processor::{CommitErrorOutcome, CommitErrorProcessor};
use crate::connection::*;
use crate::expression::QueryMetadata;
use crate::pg::metadata_lookup::{GetPgMetadataCache, PgMetadataCache};
use crate::pg::{Pg, TransactionBuilder};
use crate::query_builder::bind_collector::RawBytesBindCollector;
use crate::query_builder::*;
use crate::result::ConnectionError::CouldntSetupConfiguration;
use crate::result::*;

/// The connection string expected by `PgConnection::establish`
/// should be a PostgreSQL connection string, as documented at
/// <https://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNSTRING>
#[allow(missing_debug_implementations)]
pub struct PgConnection {
    pub(crate) raw_connection: RawConnection,
    transaction_state: AnsiTransactionManager,
    statement_cache: StatementCache<Pg, Statement>,
    metadata_cache: PgMetadataCache,
}

unsafe impl Send for PgConnection {}

impl SimpleConnection for PgConnection {
    fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
        let query = CString::new(query)?;
        let inner_result = unsafe { self.raw_connection.exec(query.as_ptr()) };
        PgResult::new(inner_result?)?;
        Ok(())
    }
}

impl<'conn, 'query> ConnectionGatWorkaround<'conn, 'query, Pg> for PgConnection {
    type Cursor = Cursor;
    type Row = self::row::PgRow;
}

impl CommitErrorProcessor for PgConnection {
    fn process_commit_error(&self, transaction_depth: i32, error: Error) -> CommitErrorOutcome {
        if matches!(
            error,
            Error::DatabaseError(DatabaseErrorKind::ClosedConnection, _)
        ) {
            return CommitErrorOutcome::Throw(error);
        }
        if transaction_depth <= 1 {
            match error {
                Error::DatabaseError(DatabaseErrorKind::ReadOnlyTransaction, _)
                | Error::DatabaseError(DatabaseErrorKind::SerializationFailure, _) => {
                    CommitErrorOutcome::RollbackAndThrow(error)
                }
                Error::AlreadyInTransaction
                | Error::DatabaseError(DatabaseErrorKind::CheckViolation, _)
                | Error::DatabaseError(DatabaseErrorKind::ClosedConnection, _)
                | Error::DatabaseError(DatabaseErrorKind::ForeignKeyViolation, _)
                | Error::DatabaseError(DatabaseErrorKind::NotNullViolation, _)
                | Error::DatabaseError(DatabaseErrorKind::UnableToSendCommand, _)
                | Error::DatabaseError(DatabaseErrorKind::UniqueViolation, _)
                | Error::DatabaseError(DatabaseErrorKind::Unknown, _)
                | Error::DeserializationError(_)
                | Error::InvalidCString(_)
                | Error::NotFound
                | Error::QueryBuilderError(_)
                | Error::RollbackError(_)
                | Error::RollbackTransaction
                | Error::SerializationError(_) => CommitErrorOutcome::Throw(error)
            }
        } else {
            let transaction_status = self.raw_connection.transaction_status();
            match error {
                Error::DatabaseError(DatabaseErrorKind::Unknown, _)
                    if transaction_status == PgTransactionStatus::InError =>
                {
                    CommitErrorOutcome::RollbackAndThrow(error)
                }
                Error::AlreadyInTransaction
                | Error::DatabaseError(DatabaseErrorKind::CheckViolation, _)
                | Error::DatabaseError(DatabaseErrorKind::ClosedConnection, _)
                | Error::DatabaseError(DatabaseErrorKind::ForeignKeyViolation, _)
                | Error::DatabaseError(DatabaseErrorKind::NotNullViolation, _)
                | Error::DatabaseError(DatabaseErrorKind::ReadOnlyTransaction, _)
                | Error::DatabaseError(DatabaseErrorKind::SerializationFailure, _)
                | Error::DatabaseError(DatabaseErrorKind::UnableToSendCommand, _)
                | Error::DatabaseError(DatabaseErrorKind::UniqueViolation, _)
                | Error::DatabaseError(DatabaseErrorKind::Unknown, _)
                | Error::DeserializationError(_)
                | Error::InvalidCString(_)
                | Error::NotFound
                | Error::QueryBuilderError(_)
                | Error::RollbackError(_)
                | Error::RollbackTransaction
                | Error::SerializationError(_) => CommitErrorOutcome::Throw(error)
            }
        }
    }
}

impl Connection for PgConnection {
    type Backend = Pg;
    type TransactionManager = AnsiTransactionManager;

    fn establish(database_url: &str) -> ConnectionResult<PgConnection> {
        RawConnection::establish(database_url).and_then(|raw_conn| {
            let mut conn = PgConnection {
                raw_connection: raw_conn,
                transaction_state: AnsiTransactionManager::default(),
                statement_cache: StatementCache::new(),
                metadata_cache: PgMetadataCache::new(),
            };
            conn.set_config_options()
                .map_err(CouldntSetupConfiguration)?;
            Ok(conn)
        })
    }

    #[doc(hidden)]
    fn execute(&mut self, query: &str) -> QueryResult<usize> {
        self.execute_inner(query).map(|res| res.rows_affected())
    }

    #[doc(hidden)]
    fn load<'conn, 'query, T>(
        &'conn mut self,
        source: T,
    ) -> QueryResult<<Self as ConnectionGatWorkaround<'conn, 'query, Pg>>::Cursor>
    where
        T: AsQuery,
        T::Query: QueryFragment<Self::Backend> + QueryId + 'query,
        Self::Backend: QueryMetadata<T::SqlType>,
    {
        self.with_prepared_query(&source.as_query(), |stmt, params, conn| {
            let result = stmt.execute(conn, &params)?;
            let cursor = Cursor::new(result);

            Ok(cursor)
        })
    }

    #[doc(hidden)]
    fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
    where
        T: QueryFragment<Pg> + QueryId,
    {
        self.with_prepared_query(source, |query, params, conn| {
            query.execute(conn, &params).map(|r| r.rows_affected())
        })
    }

    fn transaction_state(&mut self) -> &mut AnsiTransactionManager
    where
        Self: Sized,
    {
        &mut self.transaction_state
    }
}

impl GetPgMetadataCache for PgConnection {
    fn get_metadata_cache(&mut self) -> &mut PgMetadataCache {
        &mut self.metadata_cache
    }
}

impl PgConnection {
    /// Build a transaction, specifying additional details such as isolation level
    ///
    /// See [`TransactionBuilder`] for more examples.
    ///
    /// [`TransactionBuilder`]: crate::pg::TransactionBuilder
    ///
    /// ```rust
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # fn main() {
    /// #     run_test().unwrap();
    /// # }
    /// #
    /// # fn run_test() -> QueryResult<()> {
    /// #     use schema::users::dsl::*;
    /// #     let conn = &mut connection_no_transaction();
    /// conn.build_transaction()
    ///     .read_only()
    ///     .serializable()
    ///     .deferrable()
    ///     .run(|conn| Ok(()))
    /// # }
    /// ```
    pub fn build_transaction(&mut self) -> TransactionBuilder<'_, Self> {
        TransactionBuilder::new(self)
    }

    fn with_prepared_query<'conn, T: QueryFragment<Pg> + QueryId, R>(
        &'conn mut self,
        source: &'_ T,
        f: impl FnOnce(
            MaybeCached<'_, Statement>,
            Vec<Option<Vec<u8>>>,
            &'conn mut RawConnection,
        ) -> QueryResult<R>,
    ) -> QueryResult<R> {
        let mut bind_collector = RawBytesBindCollector::<Pg>::new();
        source.collect_binds(&mut bind_collector, self)?;
        let binds = bind_collector.binds;
        let metadata = bind_collector.metadata;

        let cache_len = self.statement_cache.len();
        let cache = &mut self.statement_cache;
        let raw_conn = &mut self.raw_connection;
        let query = cache.cached_statement(source, &metadata, |sql, _| {
            let query_name = if source.is_safe_to_cache_prepared()? {
                Some(format!("__diesel_stmt_{}", cache_len))
            } else {
                None
            };
            Statement::prepare(raw_conn, sql, query_name.as_deref(), &metadata)
        });

        f(query?, binds, raw_conn)
    }

    fn execute_inner(&mut self, query: &str) -> QueryResult<PgResult> {
        let query = Statement::prepare(&mut self.raw_connection, query, None, &[])?;
        query.execute(&mut self.raw_connection, &Vec::new())
    }

    fn set_config_options(&mut self) -> QueryResult<()> {
        self.execute("SET TIME ZONE 'UTC'")?;
        self.execute("SET CLIENT_ENCODING TO 'UTF8'")?;
        self.raw_connection
            .set_notice_processor(noop_notice_processor);
        Ok(())
    }
}

extern "C" fn noop_notice_processor(_: *mut libc::c_void, _message: *const libc::c_char) {}

#[cfg(test)]
mod tests {
    extern crate dotenv;

    use super::*;
    use crate::dsl::sql;
    use crate::prelude::*;
    use crate::result::Error::DatabaseError;
    use crate::sql_types::{Integer, VarChar};

    #[test]
    fn malformed_sql_query() {
        let connection = &mut connection();
        let query =
            crate::sql_query("SELECT not_existent FROM also_not_there;").execute(connection);

        if let Err(err) = query {
            if let DatabaseError(_, string) = err {
                assert_eq!(Some(26), string.statement_position());
            } else {
                unreachable!();
            }
        } else {
            unreachable!();
        }
    }

    #[test]
    fn prepared_statements_are_cached() {
        let connection = &mut connection();

        let query = crate::select(1.into_sql::<Integer>());

        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(1, connection.statement_cache.len());
    }

    #[test]
    fn queries_with_identical_sql_but_different_types_are_cached_separately() {
        let connection = &mut connection();

        let query = crate::select(1.into_sql::<Integer>());
        let query2 = crate::select("hi".into_sql::<VarChar>());

        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(Ok("hi".to_string()), query2.get_result(connection));
        assert_eq!(2, connection.statement_cache.len());
    }

    #[test]
    fn queries_with_identical_types_and_sql_but_different_bind_types_are_cached_separately() {
        let connection = &mut connection();

        let query = crate::select(1.into_sql::<Integer>()).into_boxed::<Pg>();
        let query2 = crate::select("hi".into_sql::<VarChar>()).into_boxed::<Pg>();

        assert_eq!(0, connection.statement_cache.len());
        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(Ok("hi".to_string()), query2.get_result(connection));
        assert_eq!(2, connection.statement_cache.len());
    }

    sql_function!(fn lower(x: VarChar) -> VarChar);

    #[test]
    fn queries_with_identical_types_and_binds_but_different_sql_are_cached_separately() {
        let connection = &mut connection();

        let hi = "HI".into_sql::<VarChar>();
        let query = crate::select(hi).into_boxed::<Pg>();
        let query2 = crate::select(lower(hi)).into_boxed::<Pg>();

        assert_eq!(0, connection.statement_cache.len());
        assert_eq!(Ok("HI".to_string()), query.get_result(connection));
        assert_eq!(Ok("hi".to_string()), query2.get_result(connection));
        assert_eq!(2, connection.statement_cache.len());
    }

    #[test]
    fn queries_with_sql_literal_nodes_are_not_cached() {
        let connection = &mut connection();
        let query = crate::select(sql::<Integer>("1"));

        assert_eq!(Ok(1), query.get_result(connection));
        assert_eq!(0, connection.statement_cache.len());
    }

    table! {
        users {
            id -> Integer,
            name -> Text,
        }
    }

    #[test]
    fn inserts_from_select_are_cached() {
        let connection = &mut connection();
        connection.begin_test_transaction().unwrap();

        crate::sql_query(
            "CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
        )
        .execute(connection)
        .unwrap();

        let query = users::table.filter(users::id.eq(42));
        let insert = query
            .insert_into(users::table)
            .into_columns((users::id, users::name));
        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(1, connection.statement_cache.len());

        let query = users::table.filter(users::id.eq(42)).into_boxed();
        let insert = query
            .insert_into(users::table)
            .into_columns((users::id, users::name));
        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(2, connection.statement_cache.len());
    }

    #[test]
    fn single_inserts_are_cached() {
        let connection = &mut connection();
        connection.begin_test_transaction().unwrap();

        crate::sql_query(
            "CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
        )
        .execute(connection)
        .unwrap();

        let insert =
            crate::insert_into(users::table).values((users::id.eq(42), users::name.eq("Foo")));

        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(1, connection.statement_cache.len());
    }

    #[test]
    fn dynamic_batch_inserts_are_not_cached() {
        let connection = &mut connection();
        connection.begin_test_transaction().unwrap();

        crate::sql_query(
            "CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
        )
        .execute(connection)
        .unwrap();

        let insert = crate::insert_into(users::table)
            .values(vec![(users::id.eq(42), users::name.eq("Foo"))]);

        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(0, connection.statement_cache.len());
    }

    #[test]
    fn static_batch_inserts_are_cached() {
        let connection = &mut connection();
        connection.begin_test_transaction().unwrap();

        crate::sql_query(
            "CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
        )
        .execute(connection)
        .unwrap();

        let insert =
            crate::insert_into(users::table).values([(users::id.eq(42), users::name.eq("Foo"))]);

        assert_eq!(true, insert.execute(connection).is_ok());
        assert_eq!(1, connection.statement_cache.len());
    }

    #[test]
    fn queries_containing_in_with_vec_are_cached() {
        let connection = &mut connection();
        let one_as_expr = 1.into_sql::<Integer>();
        let query = crate::select(one_as_expr.eq_any(vec![1, 2, 3]));

        assert_eq!(Ok(true), query.get_result(connection));
        assert_eq!(1, connection.statement_cache.len());
    }

    fn connection() -> PgConnection {
        crate::test_helpers::pg_connection_no_transaction()
    }
}
<|endoftext|>


<commit_before>use std::cmp::max;

pub struct Diff {
    a: String,
    b: String,
    m: usize,
    n: usize,
}

pub fn build_diff(mut a: String, mut b: String) -> Diff {
    if a.chars().count() > b.chars().count() {
        let t = a;
        a = b;
        b = t;
    }
    return Diff {
        a: a.to_string(),
        b: b.to_string(),
        m: a.chars().count(),
        n: b.chars().count(),
    };
}

impl Diff {
    fn snake(&self, mut x: usize, mut y: usize) -> usize {
        while x < self.m
            && y < self.n
            && self.a.chars().nth(x).unwrap() == self.b.chars().nth(y).unwrap()
        {
            x = x + 1;
            y = y + 1;
        }

        return y;
    }

    pub fn ed(&self) -> usize {
        let offset: usize = self.m + 1;
        let delta: usize = self.n - self.m;
        let size = self.m + self.n + 3;
        let mut fp: Vec<usize> = vec![0; size];

        let mut p: usize = 0;
        loop {
            let mut k = offset - p;
            while k <= delta + offset - 1 {
                let y = max(fp[k - 1] + 1, fp[k + 1]);
                fp[k] = self.snake(y + offset - k, y);
                k = k + 1;
            }
            let mut l = delta + p;
            while l >= delta + 1 {
                let y = max(fp[l + offset - 1] + 1, fp[l + 1 + offset]);
                fp[l + offset] = self.snake(y - l, y);
                l = l - 1;
            }

            let y = max(fp[delta + offset - 1] + 1, fp[delta + 1 + offset]);
            fp[delta + offset] = self.snake(y - delta, y);

            if fp[delta + offset] >= self.n {
                break;
            }
            p = p + 1;
        }
        return delta + 2 * p;
    }
}
<commit_msg>rust: fixed invalid calculation.<commit_after>use std::cmp::max;

pub struct Diff {
    a: String,
    b: String,
    m: usize,
    n: usize,
}

pub fn build_diff(mut a: String, mut b: String) -> Diff {
    if a.chars().count() > b.chars().count() {
        let t = a;
        a = b;
        b = t;
    }
    return Diff {
        a: a.to_string(),
        b: b.to_string(),
        m: a.chars().count(),
        n: b.chars().count(),
    };
}

impl Diff {
    fn snake(&self, mut x: isize, mut y: isize) -> isize {
        while x >= 0
            && y >= 0
            && x < self.m as isize
            && y < self.n as isize
            && self.a.chars().nth(x as usize).unwrap() == self.b.chars().nth(y as usize).unwrap()
        {
            x = x + 1;
            y = y + 1;
        }

        return y;
    }

    pub fn ed(&self) -> usize {
        let offset: usize = self.m + 1;
        let delta: usize = self.n - self.m;
        let size = self.m + self.n + 3;
        let mut fp: Vec<isize> = vec![-1; size];

        let mut p: usize = 0;
        loop {
            let mut k = offset - p;
            while k <= delta + offset - 1 {
                let y = max(fp[k - 1] + 1, fp[k + 1]);
                fp[k] = self.snake(y - (k as isize) + (offset as isize), y);
                k = k + 1;
            }
            let mut l = delta + p;
            while l >= delta + 1 {
                let y = max(fp[l + offset - 1] + 1, fp[l + 1 + offset]);
                fp[l + offset] = self.snake(y - (l as isize), y);
                l = l - 1;
            }

            let y = max(fp[delta + offset - 1] + 1, fp[delta + 1 + offset]);
            fp[delta + offset] = self.snake(y - (delta as isize), y);

            if fp[delta + offset] >= (self.n as isize) {
                break;
            }
            p = p + 1;
        }
        return delta + 2 * p;
    }
}
<|endoftext|>


<commit_before>/// Detect NIF version to build against
/// It reads from the RUSTLER_NIF_VERSION env var.
///
/// If this env var is not present we try to read from the installed Erlang.
/// If the environment doesn't have Erlang installed, then we use the latest
/// NIF version and write a warning to stderr.
use std::env;
use std::process::Command;

// keep this sorted by version number
const NIF_VERSION: &[&str] = &["2.14", "2.15", "2.16"];

fn main() {
    let latest_version = NIF_VERSION.last().unwrap().to_string();
    let version = env::var("RUSTLER_NIF_VERSION").unwrap_or_else(|_| {
        match get_version_from_erl() {
            Some(nif_version) if NIF_VERSION.contains(&nif_version.as_str()) => {
                eprintln!("RUSTLER_NIF_VERSION env var is not set. Using version from Erlang: {}", nif_version);
                nif_version
            },
            Some(ref nif_version) => panic!("The NIF version from Erlang is not supported by Rustler: {}", nif_version),
            None => {
                eprintln!("RUSTLER_NIF_VERSION env var is not set and `erl` command is not found. Using version {}", latest_version);
                latest_version
            },
        }
    });

    activate_versions(&version);

    // The following lines are important to tell Cargo to recompile if something changes.
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-env-changed=RUSTLER_NIF_VERSION");
}

fn get_version_from_erl() -> Option<String> {
    let args = vec![
        "-noshell",
        "-eval",
        r#"io:format("~s~n", [erlang:system_info(nif_version)]), init:stop()."#,
    ];

    let version = Command::new("erl").args(&args).output().ok()?.stdout;

    let version = String::from_utf8(version).ok()?;

    Some(version.trim().into())
}

fn activate_versions(version: &str) {
    let index = NIF_VERSION
        .iter()
        .position(|&v| v == version)
        .unwrap_or_else(|| {
            panic!(
                "Erlang version {} not handled, please file a a bug report.",
                version
            )
        });

    #[allow(clippy::needless_range_loop)]
    for i in 0..=index {
        println!(
            "cargo:rustc-cfg=nif_version_{}",
            version_feature(NIF_VERSION[i])
        );
    }
}

fn version_feature(version: &str) -> String {
    version.replace(".", "_")
}
<commit_msg>clippy: Fix single-pattern-char lint<commit_after>/// Detect NIF version to build against
/// It reads from the RUSTLER_NIF_VERSION env var.
///
/// If this env var is not present we try to read from the installed Erlang.
/// If the environment doesn't have Erlang installed, then we use the latest
/// NIF version and write a warning to stderr.
use std::env;
use std::process::Command;

// keep this sorted by version number
const NIF_VERSION: &[&str] = &["2.14", "2.15", "2.16"];

fn main() {
    let latest_version = NIF_VERSION.last().unwrap().to_string();
    let version = env::var("RUSTLER_NIF_VERSION").unwrap_or_else(|_| {
        match get_version_from_erl() {
            Some(nif_version) if NIF_VERSION.contains(&nif_version.as_str()) => {
                eprintln!("RUSTLER_NIF_VERSION env var is not set. Using version from Erlang: {}", nif_version);
                nif_version
            },
            Some(ref nif_version) => panic!("The NIF version from Erlang is not supported by Rustler: {}", nif_version),
            None => {
                eprintln!("RUSTLER_NIF_VERSION env var is not set and `erl` command is not found. Using version {}", latest_version);
                latest_version
            },
        }
    });

    activate_versions(&version);

    // The following lines are important to tell Cargo to recompile if something changes.
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-env-changed=RUSTLER_NIF_VERSION");
}

fn get_version_from_erl() -> Option<String> {
    let args = vec![
        "-noshell",
        "-eval",
        r#"io:format("~s~n", [erlang:system_info(nif_version)]), init:stop()."#,
    ];

    let version = Command::new("erl").args(&args).output().ok()?.stdout;

    let version = String::from_utf8(version).ok()?;

    Some(version.trim().into())
}

fn activate_versions(version: &str) {
    let index = NIF_VERSION
        .iter()
        .position(|&v| v == version)
        .unwrap_or_else(|| {
            panic!(
                "Erlang version {} not handled, please file a a bug report.",
                version
            )
        });

    #[allow(clippy::needless_range_loop)]
    for i in 0..=index {
        println!(
            "cargo:rustc-cfg=nif_version_{}",
            version_feature(NIF_VERSION[i])
        );
    }
}

fn version_feature(version: &str) -> String {
    version.replace('.', "_")
}
<|endoftext|>


<commit_before>use cocoa::base::{class, id};
use cocoa::foundation::NSUInteger;
use objc::runtime::BOOL;
use {MTLCPUCacheMode, MTLPixelFormat, MTLResourceOptions, MTLStorageMode, MTLTextureType,
     MTLTextureUsage};

/// A `MTLTextureDescriptor` object is used to configure new texture objects.
///
/// A `MTLTexture` object is specific to an individual device. To create a new texture,
/// first create a `MTLTextureDescriptor` object and set its property values, including
/// the texture’s type, size (width, height, and depth), pixel format, number of mipmap
/// levels, sample count (for multisampling), and memory allocation behavior. Then, call
/// either the `newTextureWithDescriptor:` or `newTextureWithDescriptor:iosurface:plane:`
/// method of a `MTLDevice` object, or the `newTextureWithDescriptor:offset:bytesPerRow:`
/// method of a `MTLBuffer` object.
///
/// You can reuse a `MTLTextureDescriptor` object, modifying its property values as needed,
/// to create more `MTLTexture` objects. Texture descriptor properties are used only during
/// the creation of a MTLTexture object. After the texture has been created, property
/// changes in its descriptor have no further effects on it.
///
/// For 2D and cube textures, there are convenience methods to create an MTLTextureDescriptor
/// object: `texture2DDescriptorWithPixelFormat:width:height:mipmapped:` and
/// `textureCubeDescriptorWithPixelFormat:size:mipmapped:`.
pub trait MTLTextureDescriptor {
    /// Creates a texture descriptor object for a 2D texture.
    unsafe fn texture2DDescriptorWithPixelFormat_width_height_mipmapped(
        _: Self, format: MTLPixelFormat, width: NSUInteger, height: NSUInteger,
        mipmapped: BOOL) -> id {
        msg_send![class("MTLTextureDescriptor"), texture2DDescriptorWithPixelFormat:format
                                                                              width:width
                                                                             height:height
                                                                          mipmapped:mipmapped]
    }

    /// Creates a texture descriptor object for a cube texture.
    unsafe fn textureCubeDescriptorWithPixelFormat_size_mipmapped(
        _: Self, format: MTLPixelFormat, size: NSUInteger, mipmapped: BOOL) -> id {
        msg_send![class("MTLTextureDescriptor"), textureCubeDescriptorWithPixelFormat:format
                                                                                size:size
                                                                            mipmapped:mipmapped]
    }

    /// The dimension and arrangement of texture image data.
    ///
    /// # Discussion
    ///
    /// The default value is `MTLTexture2D`.
    unsafe fn textureType(self) -> MTLTextureType;
    unsafe fn setTextureType(self, textureType: MTLTextureType);

    /// Format that determines how a pixel is written to, stored, and read from the storage
    /// allocation of the texture.
    ///
    /// # Discussion
    ///
    /// The default value is MTLPixelFormatRGBA8Unorm.
    unsafe fn pixelFormat(self) -> MTLPixelFormat;
    unsafe fn setPixelFormat(self, pixelFormat: MTLPixelFormat);

    /// The width of the texture image for the base level mipmap, in pixels.
    ///
    /// # Discussion
    ///
    /// The default value is 1. The value must be greater than or equal to `1`.
    unsafe fn width(self) -> NSUInteger;
    unsafe fn setWidth(self, width: NSUInteger);

    /// The height of the texture image for the base level mipmap, in pixels.
    ///
    /// # Discussion
    ///
    /// The default value is 1. The value must be greater than or equal to `1`.
    /// For a 1D texture, the value must be `1`.
    unsafe fn height(self) -> NSUInteger;
    unsafe fn setHeight(self, height: NSUInteger);

    /// The depth of the texture image for the base level mipmap, in pixels.
    ///
    /// # Discussion
    ///
    /// The default value is `1`. The value must be greater than or equal to `1`.
    /// For 1D, 2D, and cube textures, the value must be `1`.
    unsafe fn depth(self) -> NSUInteger;
    unsafe fn setDepth(self, depth: NSUInteger);

    /// The number of mipmap levels for this texture.
    ///
    /// # Discussion
    ///
    /// The default value is `1`. For a buffer-backed or multisample textures,
    /// the value must be `1`.
    unsafe fn mipmapLevelCount(self) -> NSUInteger;
    unsafe fn setMipmapLevelCount(self, mipmapLevelCount: NSUInteger);

    /// The number of samples in each pixel.
    ///
    /// # Discussion
    ///
    /// The default value is 1. If `textureType` is not `MTLTextureType2DMultisample`,
    /// the value must be 1.
    unsafe fn sampleCount(self) -> NSUInteger;
    unsafe fn setSampleCount(self, sampleCount: NSUInteger);

    /// The number of array elements for a `MTLTextureType1DArray` or `MTLTextureType2DArray`
    /// type texture object.
    ///
    /// # Discussion
    ///
    /// The default value is `1`. The value must be between 1 and 2048, inclusive.
    unsafe fn arrayLength(self) -> NSUInteger;
    unsafe fn setArrayLength(self, arrayLength: NSUInteger);

    /// The behavior of a new memory allocation.
    ///
    /// # Discussion
    ///
    /// This property only has an effect when you are allocating new memory. If you are
    /// creating a texture from buffer data or creating a texture view from another texture,
    /// this property value is ignored.
    unsafe fn resourceOptions(self) -> MTLResourceOptions;
    unsafe fn setResourceOptions(self, resourceOptions: MTLResourceOptions);

    /// The CPU cache mode used for the CPU mapping of the texture.
    ///
    /// # Discussion
    ///
    /// The default value is `MTLCPUCacheModeDefaultCache`.
    unsafe fn cpuCacheMode(self) -> MTLCPUCacheMode;
    unsafe fn setCpuCacheMode(self, cpuCacheMode: MTLCPUCacheMode);

    /// The storage mode used for the location and mapping of the texture.
    ///
    /// # Discsussion
    ///
    /// The default value is `MTLStorageModeAuto`.
    unsafe fn storageMode(self) -> MTLStorageMode;
    unsafe fn setStorageMode(self, storageMode: MTLStorageMode);

    /// Describes how the texture will be used in your app.
    ///
    /// # Discussion
    ///
    /// The default value is `MTLTextureUsageShaderRead` in OS X and `MTLTextureUsageUnknown` in
    /// iOS. You should always aim to determine and set specific texture usages; do not rely on
    /// the `MTLTextureUsageUnknown` value for the best performance.
    unsafe fn usage(self) -> MTLTextureUsage;
    unsafe fn setUsage(self, usage: MTLTextureUsage);
}

impl MTLTextureDescriptor for id {
    unsafe fn textureType(self) -> MTLTextureType {
        msg_send![self, textureType]
    }

    unsafe fn setTextureType(self, textureType: MTLTextureType) {
        msg_send![self, setTextureType: textureType]
    }

    unsafe fn pixelFormat(self) -> MTLPixelFormat {
        msg_send![self, pixelFormat]
    }

    unsafe fn setPixelFormat(self, pixelFormat: MTLPixelFormat) {
        msg_send![self, setPixelFormat: pixelFormat]
    }

    unsafe fn width(self) -> NSUInteger {
        msg_send![self, width]
    }

    unsafe fn setWidth(self, width: NSUInteger) {
        msg_send![self, setWidth:width]
    }

    unsafe fn height(self) -> NSUInteger {
        msg_send![self, height]
    }

    unsafe fn setHeight(self, height: NSUInteger) {
        msg_send![self, setHeight:height]
    }

    unsafe fn depth(self) -> NSUInteger {
        msg_send![self, depth]
    }

    unsafe fn setDepth(self, depth: NSUInteger) {
        msg_send![self, setDepth:depth]
    }

    unsafe fn mipmapLevelCount(self) -> NSUInteger {
        msg_send![self, mipmapLevelCount]
    }

    unsafe fn setMipmapLevelCount(self, mipmapLevelCount: NSUInteger) {
        msg_send![self, setMipmapLevelCount:mipmapLevelCount]
    }

    unsafe fn sampleCount(self) -> NSUInteger {
        msg_send![self, sampleCount]
    }

    unsafe fn setSampleCount(self, sampleCount: NSUInteger) {
        msg_send![self, setSampleCount:sampleCount]
    }

    unsafe fn arrayLength(self) -> NSUInteger {
        msg_send![self, arrayLength]
    }

    unsafe fn setArrayLength(self, arrayLength: NSUInteger) {
        msg_send![self, setArrayLength:arrayLength]
    }

    unsafe fn resourceOptions(self) -> MTLResourceOptions {
        msg_send![self, resourceOptions]
    }

    unsafe fn setResourceOptions(self, resourceOptions: MTLResourceOptions) {
        msg_send![self, setResourceOptions:resourceOptions]
    }

    unsafe fn cpuCacheMode(self) -> MTLCPUCacheMode {
        msg_send![self, cpuCacheMode]
    }

    unsafe fn setCpuCacheMode(self, cpuCacheMode: MTLCPUCacheMode) {
        msg_send![self, setCpuCacheMode:cpuCacheMode]
    }

    unsafe fn storageMode(self) -> MTLStorageMode {
        msg_send![self, storageMode]
    }

    unsafe fn setStorageMode(self, storageMode: MTLStorageMode) {
        msg_send![self, setStorageMode:storageMode]
    }

    unsafe fn usage(self) -> MTLTextureUsage {
        msg_send![self, usage]
    }

    unsafe fn setUsage(self, usage: MTLTextureUsage) {
        msg_send![self, setUsage:usage]
    }
}
<commit_msg>Implement NSCopying and NSObject methods for MTLTextureDescriptor<commit_after>use cocoa::base::{class, id};
use cocoa::foundation::NSUInteger;
use objc::runtime::BOOL;
use {MTLCPUCacheMode, MTLPixelFormat, MTLResourceOptions, MTLStorageMode, MTLTextureType,
     MTLTextureUsage};

/// A `MTLTextureDescriptor` object is used to configure new texture objects.
///
/// A `MTLTexture` object is specific to an individual device. To create a new texture,
/// first create a `MTLTextureDescriptor` object and set its property values, including
/// the texture’s type, size (width, height, and depth), pixel format, number of mipmap
/// levels, sample count (for multisampling), and memory allocation behavior. Then, call
/// either the `newTextureWithDescriptor:` or `newTextureWithDescriptor:iosurface:plane:`
/// method of a `MTLDevice` object, or the `newTextureWithDescriptor:offset:bytesPerRow:`
/// method of a `MTLBuffer` object.
///
/// You can reuse a `MTLTextureDescriptor` object, modifying its property values as needed,
/// to create more `MTLTexture` objects. Texture descriptor properties are used only during
/// the creation of a MTLTexture object. After the texture has been created, property
/// changes in its descriptor have no further effects on it.
///
/// For 2D and cube textures, there are convenience methods to create an MTLTextureDescriptor
/// object: `texture2DDescriptorWithPixelFormat:width:height:mipmapped:` and
/// `textureCubeDescriptorWithPixelFormat:size:mipmapped:`.
pub trait MTLTextureDescriptor {
    /// Creates a texture descriptor object for a 2D texture.
    unsafe fn texture2DDescriptorWithPixelFormat_width_height_mipmapped(
        _: Self, format: MTLPixelFormat, width: NSUInteger, height: NSUInteger,
        mipmapped: BOOL) -> id {
        msg_send![class("MTLTextureDescriptor"), texture2DDescriptorWithPixelFormat:format
                                                                              width:width
                                                                             height:height
                                                                          mipmapped:mipmapped]
    }

    /// Creates a texture descriptor object for a cube texture.
    unsafe fn textureCubeDescriptorWithPixelFormat_size_mipmapped(
        _: Self, format: MTLPixelFormat, size: NSUInteger, mipmapped: BOOL) -> id {
        msg_send![class("MTLTextureDescriptor"), textureCubeDescriptorWithPixelFormat:format
                                                                                size:size
                                                                            mipmapped:mipmapped]
    }

    unsafe fn new(_: Self) -> id {
        msg_send![class("MTLTextureDescriptor"), new]
    }

    /// The dimension and arrangement of texture image data.
    ///
    /// # Discussion
    ///
    /// The default value is `MTLTexture2D`.
    unsafe fn textureType(self) -> MTLTextureType;
    unsafe fn setTextureType(self, textureType: MTLTextureType);

    /// Format that determines how a pixel is written to, stored, and read from the storage
    /// allocation of the texture.
    ///
    /// # Discussion
    ///
    /// The default value is MTLPixelFormatRGBA8Unorm.
    unsafe fn pixelFormat(self) -> MTLPixelFormat;
    unsafe fn setPixelFormat(self, pixelFormat: MTLPixelFormat);

    /// The width of the texture image for the base level mipmap, in pixels.
    ///
    /// # Discussion
    ///
    /// The default value is 1. The value must be greater than or equal to `1`.
    unsafe fn width(self) -> NSUInteger;
    unsafe fn setWidth(self, width: NSUInteger);

    /// The height of the texture image for the base level mipmap, in pixels.
    ///
    /// # Discussion
    ///
    /// The default value is 1. The value must be greater than or equal to `1`.
    /// For a 1D texture, the value must be `1`.
    unsafe fn height(self) -> NSUInteger;
    unsafe fn setHeight(self, height: NSUInteger);

    /// The depth of the texture image for the base level mipmap, in pixels.
    ///
    /// # Discussion
    ///
    /// The default value is `1`. The value must be greater than or equal to `1`.
    /// For 1D, 2D, and cube textures, the value must be `1`.
    unsafe fn depth(self) -> NSUInteger;
    unsafe fn setDepth(self, depth: NSUInteger);

    /// The number of mipmap levels for this texture.
    ///
    /// # Discussion
    ///
    /// The default value is `1`. For a buffer-backed or multisample textures,
    /// the value must be `1`.
    unsafe fn mipmapLevelCount(self) -> NSUInteger;
    unsafe fn setMipmapLevelCount(self, mipmapLevelCount: NSUInteger);

    /// The number of samples in each pixel.
    ///
    /// # Discussion
    ///
    /// The default value is 1. If `textureType` is not `MTLTextureType2DMultisample`,
    /// the value must be 1.
    unsafe fn sampleCount(self) -> NSUInteger;
    unsafe fn setSampleCount(self, sampleCount: NSUInteger);

    /// The number of array elements for a `MTLTextureType1DArray` or `MTLTextureType2DArray`
    /// type texture object.
    ///
    /// # Discussion
    ///
    /// The default value is `1`. The value must be between 1 and 2048, inclusive.
    unsafe fn arrayLength(self) -> NSUInteger;
    unsafe fn setArrayLength(self, arrayLength: NSUInteger);

    /// The behavior of a new memory allocation.
    ///
    /// # Discussion
    ///
    /// This property only has an effect when you are allocating new memory. If you are
    /// creating a texture from buffer data or creating a texture view from another texture,
    /// this property value is ignored.
    unsafe fn resourceOptions(self) -> MTLResourceOptions;
    unsafe fn setResourceOptions(self, resourceOptions: MTLResourceOptions);

    /// The CPU cache mode used for the CPU mapping of the texture.
    ///
    /// # Discussion
    ///
    /// The default value is `MTLCPUCacheModeDefaultCache`.
    unsafe fn cpuCacheMode(self) -> MTLCPUCacheMode;
    unsafe fn setCpuCacheMode(self, cpuCacheMode: MTLCPUCacheMode);

    /// The storage mode used for the location and mapping of the texture.
    ///
    /// # Discsussion
    ///
    /// The default value is `MTLStorageModeAuto`.
    unsafe fn storageMode(self) -> MTLStorageMode;
    unsafe fn setStorageMode(self, storageMode: MTLStorageMode);

    /// Describes how the texture will be used in your app.
    ///
    /// # Discussion
    ///
    /// The default value is `MTLTextureUsageShaderRead` in OS X and `MTLTextureUsageUnknown` in
    /// iOS. You should always aim to determine and set specific texture usages; do not rely on
    /// the `MTLTextureUsageUnknown` value for the best performance.
    unsafe fn usage(self) -> MTLTextureUsage;
    unsafe fn setUsage(self, usage: MTLTextureUsage);

    unsafe fn copy(self) -> id;
}

impl MTLTextureDescriptor for id {
    unsafe fn textureType(self) -> MTLTextureType {
        msg_send![self, textureType]
    }

    unsafe fn setTextureType(self, textureType: MTLTextureType) {
        msg_send![self, setTextureType: textureType]
    }

    unsafe fn pixelFormat(self) -> MTLPixelFormat {
        msg_send![self, pixelFormat]
    }

    unsafe fn setPixelFormat(self, pixelFormat: MTLPixelFormat) {
        msg_send![self, setPixelFormat: pixelFormat]
    }

    unsafe fn width(self) -> NSUInteger {
        msg_send![self, width]
    }

    unsafe fn setWidth(self, width: NSUInteger) {
        msg_send![self, setWidth:width]
    }

    unsafe fn height(self) -> NSUInteger {
        msg_send![self, height]
    }

    unsafe fn setHeight(self, height: NSUInteger) {
        msg_send![self, setHeight:height]
    }

    unsafe fn depth(self) -> NSUInteger {
        msg_send![self, depth]
    }

    unsafe fn setDepth(self, depth: NSUInteger) {
        msg_send![self, setDepth:depth]
    }

    unsafe fn mipmapLevelCount(self) -> NSUInteger {
        msg_send![self, mipmapLevelCount]
    }

    unsafe fn setMipmapLevelCount(self, mipmapLevelCount: NSUInteger) {
        msg_send![self, setMipmapLevelCount:mipmapLevelCount]
    }

    unsafe fn sampleCount(self) -> NSUInteger {
        msg_send![self, sampleCount]
    }

    unsafe fn setSampleCount(self, sampleCount: NSUInteger) {
        msg_send![self, setSampleCount:sampleCount]
    }

    unsafe fn arrayLength(self) -> NSUInteger {
        msg_send![self, arrayLength]
    }

    unsafe fn setArrayLength(self, arrayLength: NSUInteger) {
        msg_send![self, setArrayLength:arrayLength]
    }

    unsafe fn resourceOptions(self) -> MTLResourceOptions {
        msg_send![self, resourceOptions]
    }

    unsafe fn setResourceOptions(self, resourceOptions: MTLResourceOptions) {
        msg_send![self, setResourceOptions:resourceOptions]
    }

    unsafe fn cpuCacheMode(self) -> MTLCPUCacheMode {
        msg_send![self, cpuCacheMode]
    }

    unsafe fn setCpuCacheMode(self, cpuCacheMode: MTLCPUCacheMode) {
        msg_send![self, setCpuCacheMode:cpuCacheMode]
    }

    unsafe fn storageMode(self) -> MTLStorageMode {
        msg_send![self, storageMode]
    }

    unsafe fn setStorageMode(self, storageMode: MTLStorageMode) {
        msg_send![self, setStorageMode:storageMode]
    }

    unsafe fn usage(self) -> MTLTextureUsage {
        msg_send![self, usage]
    }

    unsafe fn setUsage(self, usage: MTLTextureUsage) {
        msg_send![self, setUsage:usage]
    }

    unsafe fn copy(self) -> id {
        msg_send![self, copy]
    }
}
<|endoftext|>