<commit_before>// Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0.

use crate::resource_metering::test_suite::MockReceiverServer;

use std::sync::Arc;
use std::time::Duration;

use concurrency_manager::ConcurrencyManager;
use crossbeam::channel::{unbounded, Receiver, RecvTimeoutError, Sender};
use engine_rocks::PerfLevel;
use grpcio::{ChannelBuilder, Environment};
use kvproto::coprocessor;
use kvproto::kvrpcpb::*;
use kvproto::resource_usage_agent::ResourceUsageRecord;
use kvproto::tikvpb::*;
use protobuf::Message;
use resource_metering::ResourceTagFactory;
use test_coprocessor::{DAGSelect, ProductTable, Store};
use test_raftstore::*;
use test_util::alloc_port;
use tidb_query_datatype::codec::Datum;
use tikv::config::CoprReadPoolConfig;
use tikv::coprocessor::{readpool_impl, Endpoint};
use tikv::read_pool::ReadPool;
use tikv::storage::{Engine, RocksEngine, TestEngineBuilder};
use tikv_util::config::ReadableDuration;
use tikv_util::thread_group::GroupProperties;
use tikv_util::HandyRwLock;
use tipb::SelectResponse;

#[test]
pub fn test_read_keys() {
    // Create & start receiver server.
    let (tx, rx) = unbounded();
    let mut server = MockReceiverServer::new(tx);
    let port = alloc_port();
    let env = Arc::new(Environment::new(1));
    server.start_server(port, env.clone());

    // Create cluster.
    let (_cluster, client, mut ctx) = new_cluster(port, env);

    // Set resource group tag for enable resource metering.
    ctx.set_resource_group_tag("TEST-TAG".into());

    let mut ts = 0;

    // Write 10 key-value pairs.
    for n in 0..10 {
        let n = n.to_string().into_bytes();
        let (k, v) = (n.clone(), n);

        // Prewrite.
        ts += 1;
        let prewrite_start_version = ts;
        let mut mutation = Mutation::default();
        mutation.set_op(Op::Put);
        mutation.set_key(k.clone());
        mutation.set_value(v.clone());
        must_kv_prewrite(
            &client,
            ctx.clone(),
            vec![mutation],
            k.clone(),
            prewrite_start_version,
        );

        // Commit.
        ts += 1;
        let commit_version = ts;
        must_kv_commit(
            &client,
            ctx.clone(),
            vec![k.clone()],
            prewrite_start_version,
            commit_version,
            commit_version,
        );
    }

    // PointGet
    ts += 1;
    let mut get_req = GetRequest::default();
    get_req.set_context(ctx.clone());
    get_req.set_key(b"0".to_vec());
    get_req.set_version(ts);
    let _ = client.kv_get(&get_req).unwrap(); // trigger thread register
    std::thread::sleep(Duration::from_secs(2));
    recv_read_keys(&rx);
    let get_resp = client.kv_get(&get_req).unwrap();
    assert!(!get_resp.has_region_error());
    assert!(!get_resp.has_error());
    let scan_detail_v2 = get_resp.get_exec_details_v2().get_scan_detail_v2();
    assert_eq!(scan_detail_v2.get_total_versions(), 1);
    assert_eq!(scan_detail_v2.get_processed_versions(), 1);
    assert!(scan_detail_v2.get_processed_versions_size() > 0);
    assert_eq!(get_resp.value, b"0".to_vec());

    // Wait & receive & assert.
    assert_eq!(must_recv_read_keys(&rx), 1);

    // Scan 0 ~ 4.
    ts += 1;
    let mut scan_req = ScanRequest::default();
    scan_req.set_context(ctx.clone());
    scan_req.set_start_key(b"0".to_vec());
    scan_req.set_limit(5);
    scan_req.set_version(ts);
    let scan_resp = client.kv_scan(&scan_req).unwrap();
    assert!(!scan_resp.has_region_error());
    assert_eq!(scan_resp.pairs.len(), 5);

    // Wait & receive & assert.
    assert_eq!(must_recv_read_keys(&rx), 5);

    // Scan 0 ~ 9.
    ts += 1;
    let mut scan_req = ScanRequest::default();
    scan_req.set_context(ctx.clone());
    scan_req.set_start_key(b"0".to_vec());
    scan_req.set_limit(100);
    scan_req.set_version(ts);
    let scan_resp = client.kv_scan(&scan_req).unwrap();
    assert!(!scan_resp.has_region_error());
    assert_eq!(scan_resp.pairs.len(), 10);

    // Wait & receive & assert.
    assert_eq!(must_recv_read_keys(&rx), 10);

    // Shutdown receiver server.
    tokio::runtime::Runtime::new().unwrap().block_on(async {
        server.shutdown_server().await;
    });
}

fn new_cluster(port: u16, env: Arc<Environment>) -> (Cluster<ServerCluster>, TikvClient, Context) {
    let (cluster, leader, ctx) = must_new_and_configure_cluster(|cluster| {
        cluster.cfg.resource_metering.receiver_address = format!("127.0.0.1:{}", port);
        cluster.cfg.resource_metering.precision = ReadableDuration::millis(100);
        cluster.cfg.resource_metering.report_receiver_interval = ReadableDuration::millis(400);
    });
    let channel =
        ChannelBuilder::new(env).connect(&cluster.sim.rl().get_addr(leader.get_store_id()));
    let client = TikvClient::new(channel);
    (cluster, client, ctx)
}

fn must_recv_read_keys(rx: &Receiver<Vec<ResourceUsageRecord>>) -> u32 {
    const MAX_WAIT_SECS: u32 = 30;
    let duration = Duration::from_secs(1);
    for _ in 0..MAX_WAIT_SECS {
        std::thread::sleep(duration);
        let read_keys = recv_read_keys(rx);
        if read_keys > 0 {
            return read_keys;
        }
    }
    panic!("no read_keys");
}

fn recv_read_keys(rx: &Receiver<Vec<ResourceUsageRecord>>) -> u32 {
    let mut total = 0;
    while let Ok(records) = rx.try_recv() {
        for r in &records {
            total += r
                .get_record()
                .get_items()
                .iter()
                .map(|item| item.read_keys)
                .sum::<u32>();
        }
    }
    total
}

#[test]
fn test_read_keys_coprocessor() {
    // Start resource metering.
    let mut cfg = resource_metering::Config::default();
    cfg.precision = ReadableDuration::millis(100);
    cfg.report_receiver_interval = ReadableDuration::millis(400);

    let (_, collector_reg_handle, resource_tag_factory, recorder_worker) =
        resource_metering::init_recorder(cfg.precision.as_millis());
    let (_, data_sink_reg_handle, reporter_worker) =
        resource_metering::init_reporter(cfg, collector_reg_handle);

    let data_sink = MockDataSink::new();
    let _reg_guard = data_sink_reg_handle.register(Box::new(data_sink.clone()));

    // Init data.
    let data = vec![
        (1, Some("name:0"), 2),
        (2, Some("name:4"), 3),
        (4, Some("name:3"), 1),
        (5, Some("name:1"), 4),
    ];
    let product = ProductTable::new();
    let endpoint = init_coprocessor_with_data(&product, &data, resource_tag_factory);
    let runtime = tokio::runtime::Builder::new_current_thread()
        .build()
        .unwrap();

    // Do DAG select to register runtime thread.
    let mut req = DAGSelect::from(&product).build();
    let mut ctx = Context::default();
    ctx.set_resource_group_tag("TEST-TAG".into());
    req.set_context(ctx);
    runtime.block_on(handle_select(&endpoint, req.clone()));

    // Clear current result.
    let _ = data_sink.wait_read_keys(Duration::from_secs(3));

    // Do DAG select again.
    runtime.block_on(handle_select(&endpoint, req));

    // Wait & receive & assert.
    assert_eq!(
        data_sink.wait_read_keys(Duration::from_secs(30)).unwrap(),
        4
    );

    // Cleanup.
    reporter_worker.stop_worker();
    recorder_worker.stop_worker();
}

fn init_coprocessor_with_data(
    tbl: &ProductTable,
    vals: &[(i64, Option<&str>, i64)],
    tag_factory: ResourceTagFactory,
) -> Endpoint<RocksEngine> {
    let engine = TestEngineBuilder::new().build().unwrap();
    let mut store = Store::from_engine(engine);
    store.begin();
    for &(id, name, count) in vals {
        store
            .insert_into(tbl)
            .set(&tbl["id"], Datum::I64(id))
            .set(&tbl["name"], name.map(str::as_bytes).into())
            .set(&tbl["count"], Datum::I64(count))
            .execute_with_ctx(Context::default());
    }
    store.commit_with_ctx(Context::default());
    tikv_util::thread_group::set_properties(Some(GroupProperties::default()));
    let pool = ReadPool::from(readpool_impl::build_read_pool_for_test(
        &CoprReadPoolConfig::default_for_test(),
        store.get_engine(),
    ));
    let cm = ConcurrencyManager::new(1.into());
    Endpoint::new(
        &tikv::server::Config::default(),
        pool.handle(),
        cm,
        PerfLevel::EnableCount,
        tag_factory,
    )
}

async fn handle_select<E>(copr: &Endpoint<E>, req: coprocessor::Request) -> SelectResponse
where
    E: Engine,
{
    let resp = copr
        .parse_and_handle_unary_request(req, None)
        .await
        .consume();
    assert!(!resp.get_data().is_empty(), "{:?}", resp);
    let mut sel_resp = SelectResponse::default();
    sel_resp.merge_from_bytes(resp.get_data()).unwrap();
    sel_resp
}

#[derive(Clone)]
struct MockDataSink {
    tx: Sender<u32>,
    rx: Receiver<u32>,
}

impl MockDataSink {
    fn new() -> Self {
        let (tx, rx) = unbounded();
        Self { tx, rx }
    }

    fn wait_read_keys(&self, timeout: Duration) -> Result<u32, RecvTimeoutError> {
        self.rx.recv_timeout(timeout)
    }
}

impl resource_metering::DataSink for MockDataSink {
    fn try_send(
        &mut self,
        records: Arc<Vec<ResourceUsageRecord>>,
    ) -> resource_metering::error::Result<()> {
        let mut read_keys = 0;
        for r in records.iter() {
            read_keys += r
                .get_record()
                .get_items()
                .iter()
                .map(|item| item.read_keys)
                .sum::<u32>();
        }
        self.tx.send(read_keys).unwrap();
        Ok(())
    }
}
<commit_msg>res_meter: ignore the read keys test temporarily (#11779)<commit_after>// Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0.

use crate::resource_metering::test_suite::MockReceiverServer;

use std::sync::Arc;
use std::time::Duration;

use concurrency_manager::ConcurrencyManager;
use crossbeam::channel::{unbounded, Receiver, RecvTimeoutError, Sender};
use engine_rocks::PerfLevel;
use grpcio::{ChannelBuilder, Environment};
use kvproto::coprocessor;
use kvproto::kvrpcpb::*;
use kvproto::resource_usage_agent::ResourceUsageRecord;
use kvproto::tikvpb::*;
use protobuf::Message;
use resource_metering::ResourceTagFactory;
use test_coprocessor::{DAGSelect, ProductTable, Store};
use test_raftstore::*;
use test_util::alloc_port;
use tidb_query_datatype::codec::Datum;
use tikv::config::CoprReadPoolConfig;
use tikv::coprocessor::{readpool_impl, Endpoint};
use tikv::read_pool::ReadPool;
use tikv::storage::{Engine, RocksEngine, TestEngineBuilder};
use tikv_util::config::ReadableDuration;
use tikv_util::thread_group::GroupProperties;
use tikv_util::HandyRwLock;
use tipb::SelectResponse;

#[test]
#[ignore = "the case is unstable, ref #11765"]
pub fn test_read_keys() {
    // Create & start receiver server.
    let (tx, rx) = unbounded();
    let mut server = MockReceiverServer::new(tx);
    let port = alloc_port();
    let env = Arc::new(Environment::new(1));
    server.start_server(port, env.clone());

    // Create cluster.
    let (_cluster, client, mut ctx) = new_cluster(port, env);

    // Set resource group tag for enable resource metering.
    ctx.set_resource_group_tag("TEST-TAG".into());

    let mut ts = 0;

    // Write 10 key-value pairs.
    for n in 0..10 {
        let n = n.to_string().into_bytes();
        let (k, v) = (n.clone(), n);

        // Prewrite.
        ts += 1;
        let prewrite_start_version = ts;
        let mut mutation = Mutation::default();
        mutation.set_op(Op::Put);
        mutation.set_key(k.clone());
        mutation.set_value(v.clone());
        must_kv_prewrite(
            &client,
            ctx.clone(),
            vec![mutation],
            k.clone(),
            prewrite_start_version,
        );

        // Commit.
        ts += 1;
        let commit_version = ts;
        must_kv_commit(
            &client,
            ctx.clone(),
            vec![k.clone()],
            prewrite_start_version,
            commit_version,
            commit_version,
        );
    }

    // PointGet
    ts += 1;
    let mut get_req = GetRequest::default();
    get_req.set_context(ctx.clone());
    get_req.set_key(b"0".to_vec());
    get_req.set_version(ts);
    let _ = client.kv_get(&get_req).unwrap(); // trigger thread register
    std::thread::sleep(Duration::from_secs(2));
    recv_read_keys(&rx);
    let get_resp = client.kv_get(&get_req).unwrap();
    assert!(!get_resp.has_region_error());
    assert!(!get_resp.has_error());
    let scan_detail_v2 = get_resp.get_exec_details_v2().get_scan_detail_v2();
    assert_eq!(scan_detail_v2.get_total_versions(), 1);
    assert_eq!(scan_detail_v2.get_processed_versions(), 1);
    assert!(scan_detail_v2.get_processed_versions_size() > 0);
    assert_eq!(get_resp.value, b"0".to_vec());

    // Wait & receive & assert.
    assert_eq!(must_recv_read_keys(&rx), 1);

    // Scan 0 ~ 4.
    ts += 1;
    let mut scan_req = ScanRequest::default();
    scan_req.set_context(ctx.clone());
    scan_req.set_start_key(b"0".to_vec());
    scan_req.set_limit(5);
    scan_req.set_version(ts);
    let scan_resp = client.kv_scan(&scan_req).unwrap();
    assert!(!scan_resp.has_region_error());
    assert_eq!(scan_resp.pairs.len(), 5);

    // Wait & receive & assert.
    assert_eq!(must_recv_read_keys(&rx), 5);

    // Scan 0 ~ 9.
    ts += 1;
    let mut scan_req = ScanRequest::default();
    scan_req.set_context(ctx.clone());
    scan_req.set_start_key(b"0".to_vec());
    scan_req.set_limit(100);
    scan_req.set_version(ts);
    let scan_resp = client.kv_scan(&scan_req).unwrap();
    assert!(!scan_resp.has_region_error());
    assert_eq!(scan_resp.pairs.len(), 10);

    // Wait & receive & assert.
    assert_eq!(must_recv_read_keys(&rx), 10);

    // Shutdown receiver server.
    tokio::runtime::Runtime::new().unwrap().block_on(async {
        server.shutdown_server().await;
    });
}

fn new_cluster(port: u16, env: Arc<Environment>) -> (Cluster<ServerCluster>, TikvClient, Context) {
    let (cluster, leader, ctx) = must_new_and_configure_cluster(|cluster| {
        cluster.cfg.resource_metering.receiver_address = format!("127.0.0.1:{}", port);
        cluster.cfg.resource_metering.precision = ReadableDuration::millis(100);
        cluster.cfg.resource_metering.report_receiver_interval = ReadableDuration::millis(400);
    });
    let channel =
        ChannelBuilder::new(env).connect(&cluster.sim.rl().get_addr(leader.get_store_id()));
    let client = TikvClient::new(channel);
    (cluster, client, ctx)
}

fn must_recv_read_keys(rx: &Receiver<Vec<ResourceUsageRecord>>) -> u32 {
    const MAX_WAIT_SECS: u32 = 30;
    let duration = Duration::from_secs(1);
    for _ in 0..MAX_WAIT_SECS {
        std::thread::sleep(duration);
        let read_keys = recv_read_keys(rx);
        if read_keys > 0 {
            return read_keys;
        }
    }
    panic!("no read_keys");
}

fn recv_read_keys(rx: &Receiver<Vec<ResourceUsageRecord>>) -> u32 {
    let mut total = 0;
    while let Ok(records) = rx.try_recv() {
        for r in &records {
            total += r
                .get_record()
                .get_items()
                .iter()
                .map(|item| item.read_keys)
                .sum::<u32>();
        }
    }
    total
}

#[test]
#[ignore = "the case is unstable, ref #11765"]
fn test_read_keys_coprocessor() {
    // Start resource metering.
    let mut cfg = resource_metering::Config::default();
    cfg.precision = ReadableDuration::millis(100);
    cfg.report_receiver_interval = ReadableDuration::millis(400);

    let (_, collector_reg_handle, resource_tag_factory, recorder_worker) =
        resource_metering::init_recorder(cfg.precision.as_millis());
    let (_, data_sink_reg_handle, reporter_worker) =
        resource_metering::init_reporter(cfg, collector_reg_handle);

    let data_sink = MockDataSink::new();
    let _reg_guard = data_sink_reg_handle.register(Box::new(data_sink.clone()));

    // Init data.
    let data = vec![
        (1, Some("name:0"), 2),
        (2, Some("name:4"), 3),
        (4, Some("name:3"), 1),
        (5, Some("name:1"), 4),
    ];
    let product = ProductTable::new();
    let endpoint = init_coprocessor_with_data(&product, &data, resource_tag_factory);
    let runtime = tokio::runtime::Builder::new_current_thread()
        .build()
        .unwrap();

    // Do DAG select to register runtime thread.
    let mut req = DAGSelect::from(&product).build();
    let mut ctx = Context::default();
    ctx.set_resource_group_tag("TEST-TAG".into());
    req.set_context(ctx);
    runtime.block_on(handle_select(&endpoint, req.clone()));

    // Clear current result.
    let _ = data_sink.wait_read_keys(Duration::from_secs(3));

    // Do DAG select again.
    runtime.block_on(handle_select(&endpoint, req));

    // Wait & receive & assert.
    assert_eq!(
        data_sink.wait_read_keys(Duration::from_secs(30)).unwrap(),
        4
    );

    // Cleanup.
    reporter_worker.stop_worker();
    recorder_worker.stop_worker();
}

fn init_coprocessor_with_data(
    tbl: &ProductTable,
    vals: &[(i64, Option<&str>, i64)],
    tag_factory: ResourceTagFactory,
) -> Endpoint<RocksEngine> {
    let engine = TestEngineBuilder::new().build().unwrap();
    let mut store = Store::from_engine(engine);
    store.begin();
    for &(id, name, count) in vals {
        store
            .insert_into(tbl)
            .set(&tbl["id"], Datum::I64(id))
            .set(&tbl["name"], name.map(str::as_bytes).into())
            .set(&tbl["count"], Datum::I64(count))
            .execute_with_ctx(Context::default());
    }
    store.commit_with_ctx(Context::default());
    tikv_util::thread_group::set_properties(Some(GroupProperties::default()));
    let pool = ReadPool::from(readpool_impl::build_read_pool_for_test(
        &CoprReadPoolConfig::default_for_test(),
        store.get_engine(),
    ));
    let cm = ConcurrencyManager::new(1.into());
    Endpoint::new(
        &tikv::server::Config::default(),
        pool.handle(),
        cm,
        PerfLevel::EnableCount,
        tag_factory,
    )
}

async fn handle_select<E>(copr: &Endpoint<E>, req: coprocessor::Request) -> SelectResponse
where
    E: Engine,
{
    let resp = copr
        .parse_and_handle_unary_request(req, None)
        .await
        .consume();
    assert!(!resp.get_data().is_empty(), "{:?}", resp);
    let mut sel_resp = SelectResponse::default();
    sel_resp.merge_from_bytes(resp.get_data()).unwrap();
    sel_resp
}

#[derive(Clone)]
struct MockDataSink {
    tx: Sender<u32>,
    rx: Receiver<u32>,
}

impl MockDataSink {
    fn new() -> Self {
        let (tx, rx) = unbounded();
        Self { tx, rx }
    }

    fn wait_read_keys(&self, timeout: Duration) -> Result<u32, RecvTimeoutError> {
        self.rx.recv_timeout(timeout)
    }
}

impl resource_metering::DataSink for MockDataSink {
    fn try_send(
        &mut self,
        records: Arc<Vec<ResourceUsageRecord>>,
    ) -> resource_metering::error::Result<()> {
        let mut read_keys = 0;
        for r in records.iter() {
            read_keys += r
                .get_record()
                .get_items()
                .iter()
                .map(|item| item.read_keys)
                .sum::<u32>();
        }
        self.tx.send(read_keys).unwrap();
        Ok(())
    }
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2020, R. Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::*;
use syn::*;
use syn::parse::*;
use syn::punctuated::*;

mod kw {
    syn::custom_keyword!(default);
}

#[derive(Debug)]
struct BitPatternDefaultExpr {
    _ident: Ident,
    _eq: syn::token::Eq,
    expr: Expr,
}

impl Parse for BitPatternDefaultExpr {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        Ok(BitPatternDefaultExpr{
            _ident: input.parse()?,
            _eq: input.parse()?,
            expr: input.parse()?,
        })
    }
}

#[derive(Debug)]
enum BitPatternSetting {
    DefaultExpr(BitPatternDefaultExpr),
}

impl Parse for BitPatternSetting {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(kw::default) {
            input.parse().map(BitPatternSetting::DefaultExpr)
        } else {
            Err(lookahead.error())
        }
    }
}

#[derive(Debug)]
struct BitPatternSettings(Punctuated<BitPatternSetting, token::Comma>);

impl Parse for BitPatternSettings {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        Ok(BitPatternSettings(input.parse_terminated(BitPatternSetting::parse)?))
    }
}

pub fn bitpattern(args: TokenStream, input: TokenStream) -> TokenStream {
    let args = parse_macro_input!(args as BitPatternSettings);
    let mut input = parse_macro_input!(input as ItemEnum);

    println!("{:?}", args);

    let mut default_expr = None;

    // process args
    for arg in args.0 {
        match arg {
            BitPatternSetting::DefaultExpr(bpdx) => {
                default_expr = Some(bpdx.expr);
            }
        }
    }

    // Ignore enums with no variants
    if input.variants.len() == 0 {
        println!("Warning: BitPattern used on enum with no variants");
        return TokenStream::from(quote!{#input});
    }

    let enum_id = input.ident.clone();

    // loop over the variants and grab the necessary data
    let mut var_data = Vec::new();
    for var in &mut input.variants {
        let var_id = var.ident.clone();

        if var.fields != Fields::Unit {
            panic!("Variant {} must be a unit variant", var_id.to_string());
        }

        // Find the #[bits] attribute and docstrings
        let mut bits_attrib = None;
        let mut bits_docs = String::new();
        for (i, attr) in var.attrs.iter().enumerate() {
            if attr.path.is_ident("bits") {
                if bits_attrib.is_some() {
                    panic!("Only one #[bits] attribute allowed on {}", var_id.to_string());
                }

                let bits_arg = attr.parse_args::<LitStr>();
                if let Err(e) = bits_arg {
                    return e.to_compile_error().into();
                }
                let bits_arg = bits_arg.unwrap();

                bits_attrib = Some((i, bits_arg.value()));
            }

            if attr.path.is_ident("doc") {
                let doc_meta = attr.parse_meta();
                if let Err(e) = doc_meta {
                    return e.to_compile_error().into();
                }
                let doc_meta = doc_meta.unwrap();

                if let Meta::NameValue(nv) = doc_meta {
                    if let Lit::Str(s) = nv.lit {
                        if bits_docs.len() != 0 {
                            bits_docs.push_str(" ");
                        }
                        bits_docs.push_str(s.value().trim());
                    }
                }
            }
        }
        if bits_attrib.is_none() {
            panic!("Variant {} must have a #[bits] attribute", var_id.to_string());
        }

        let (bits_attrib_i, bits_string) = bits_attrib.unwrap();
        var.attrs.remove(bits_attrib_i);

        var_data.push((var_id, bits_string, bits_docs));
    }

    // Gathered all variants and their settings, do some checks to make sure
    // things are valid
    let num_bits = var_data[0].1.len();
    for (_, bits_string, _) in &var_data {
        if bits_string.len() != num_bits {
            panic!("All bits need to be the same length");
        }

        for c in bits_string.chars() {
            if c != '0'  && c != '1' && c != 'x' && c != 'X' {
                panic!("Illegal character in bits attribute");
            }
        }
    }

    // Can start generating code now
    // Dummy for now
    let bit_names = (0..num_bits).map(|x| LitStr::new(&x.to_string(), Span::call_site()));
    let bit_names2 = (0..num_bits).map(|x| LitStr::new(&x.to_string(), Span::call_site()));
    let bit_nums = 0..num_bits;

    // For encode function
    let encode_values = var_data.iter().map(|x|
        x.1.chars().map(|c|
            match c {
                '0'|'x' => quote! {false},
                '1'|'X' => quote! {true},
                _ => unreachable!(),
            }
        ).collect::<Vec<_>>()
    );
    let encode_var_id = var_data.iter().map(|x| x.0.clone());

    // For decode function
    let decode_values = var_data.iter().map(|x|
        x.1.chars().map(|c|
            match c {
                '0' => quote! {false},
                '1' => quote! {true},
                'x'|'X' => quote! {_},
                _ => unreachable!(),
            }
        ).collect::<Vec<_>>()
    );
    let decode_var_id = var_data.iter().map(|x| x.0.clone());

    let default_expr = default_expr.iter();

    // For docs
    let variant_names = var_data.iter().map(|x| LitStr::new(&x.0.to_string(), Span::call_site()));
    let variant_docs = var_data.iter().map(|x| LitStr::new(&x.2.to_string(), Span::call_site()));
    let variant_bits = var_data.iter().map(|x| LitStr::new(&x.1.to_string(), Span::call_site()));

    let output = quote!{
        #input

        impl ::bittwiddler::BitPattern for #enum_id {
            type BitsArrType = [bool; #num_bits];
            const BITS_COUNT: usize = #num_bits;
            type ErrType = ();  // TODO

            type VarNamesIterType = ::std::slice::Iter<'static, &'static str>;
            type VarDescsIterType = ::std::slice::Iter<'static, &'static str>;
            type VarBitsIterType = ::std::slice::Iter<'static, &'static str>;

            fn encode(&self) -> Self::BitsArrType {
                match self {
                    #(Self::#encode_var_id => [#(#encode_values),*]),*
                }
            }

            fn decode(bits: Self::BitsArrType) -> Result<Self, Self::ErrType> {
                match bits {
                    #([#(#decode_values),*] => Ok(Self::#decode_var_id)),*
                    #(,_ => Ok(#default_expr))*
                }
            }

            fn _pos_to_name(pos: usize) -> &'static str {
                [#(#bit_names),*][pos]
            }

            fn _name_to_pos(name: &'static str) -> usize {
                match name {
                    #(#bit_names2 => #bit_nums),*
                    ,_ => unreachable!()
                }
            }

            fn variantnames() -> Self::VarNamesIterType {
                [#(#variant_names),*].iter()
            }

            fn variantdescs() -> Self::VarDescsIterType {
                [#(#variant_docs),*].iter()
            }

            fn variantbits() -> Self::VarBitsIterType {
                [#(#variant_bits),*].iter()
            }
        }
    };

    TokenStream::from(output)
}
<commit_msg>bittwiddler: Reimplement errtype<commit_after>/*
Copyright (c) 2020, R. Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::*;
use syn::*;
use syn::parse::*;
use syn::punctuated::*;

mod kw {
    syn::custom_keyword!(default);
    syn::custom_keyword!(errtype);
}

#[derive(Debug)]
struct BitPatternDefaultExpr {
    _ident: Ident,
    _eq: syn::token::Eq,
    expr: Expr,
}

impl Parse for BitPatternDefaultExpr {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        Ok(BitPatternDefaultExpr{
            _ident: input.parse()?,
            _eq: input.parse()?,
            expr: input.parse()?,
        })
    }
}

#[derive(Debug)]
struct BitPatternErrType {
    _ident: Ident,
    _eq: syn::token::Eq,
    ty: Type,
}

impl Parse for BitPatternErrType {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        Ok(BitPatternErrType{
            _ident: input.parse()?,
            _eq: input.parse()?,
            ty: input.parse()?,
        })
    }
}

#[derive(Debug)]
enum BitPatternSetting {
    DefaultExpr(BitPatternDefaultExpr),
    ErrType(BitPatternErrType),
}

impl Parse for BitPatternSetting {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(kw::default) {
            input.parse().map(BitPatternSetting::DefaultExpr)
        } else if lookahead.peek(kw::errtype) {
            input.parse().map(BitPatternSetting::ErrType)
        } else {
            Err(lookahead.error())
        }
    }
}

#[derive(Debug)]
struct BitPatternSettings(Punctuated<BitPatternSetting, token::Comma>);

impl Parse for BitPatternSettings {
    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
        Ok(BitPatternSettings(input.parse_terminated(BitPatternSetting::parse)?))
    }
}

pub fn bitpattern(args: TokenStream, input: TokenStream) -> TokenStream {
    let args = parse_macro_input!(args as BitPatternSettings);
    let mut input = parse_macro_input!(input as ItemEnum);

    let mut default_expr = None;
    let mut errtype = None;

    // process args
    for arg in args.0 {
        match arg {
            BitPatternSetting::DefaultExpr(bpdx) => {
                default_expr = Some(bpdx.expr);
            },
            BitPatternSetting::ErrType(bpet) => {
                errtype = Some(bpet.ty);
            }
        }
    }

    // Ignore enums with no variants
    if input.variants.len() == 0 {
        println!("Warning: BitPattern used on enum with no variants");
        return TokenStream::from(quote!{#input});
    }

    let enum_id = input.ident.clone();

    // loop over the variants and grab the necessary data
    let mut var_data = Vec::new();
    for var in &mut input.variants {
        let var_id = var.ident.clone();

        if var.fields != Fields::Unit {
            panic!("Variant {} must be a unit variant", var_id.to_string());
        }

        // Find the #[bits] attribute and docstrings
        let mut bits_attrib = None;
        let mut bits_docs = String::new();
        for (i, attr) in var.attrs.iter().enumerate() {
            if attr.path.is_ident("bits") {
                if bits_attrib.is_some() {
                    panic!("Only one #[bits] attribute allowed on {}", var_id.to_string());
                }

                let bits_arg = attr.parse_args::<LitStr>();
                if let Err(e) = bits_arg {
                    return e.to_compile_error().into();
                }
                let bits_arg = bits_arg.unwrap();

                bits_attrib = Some((i, bits_arg.value()));
            }

            if attr.path.is_ident("doc") {
                let doc_meta = attr.parse_meta();
                if let Err(e) = doc_meta {
                    return e.to_compile_error().into();
                }
                let doc_meta = doc_meta.unwrap();

                if let Meta::NameValue(nv) = doc_meta {
                    if let Lit::Str(s) = nv.lit {
                        if bits_docs.len() != 0 {
                            bits_docs.push_str(" ");
                        }
                        bits_docs.push_str(s.value().trim());
                    }
                }
            }
        }
        if bits_attrib.is_none() {
            panic!("Variant {} must have a #[bits] attribute", var_id.to_string());
        }

        let (bits_attrib_i, bits_string) = bits_attrib.unwrap();
        var.attrs.remove(bits_attrib_i);

        var_data.push((var_id, bits_string, bits_docs));
    }

    // Gathered all variants and their settings, do some checks to make sure
    // things are valid
    let num_bits = var_data[0].1.len();
    for (_, bits_string, _) in &var_data {
        if bits_string.len() != num_bits {
            panic!("All bits need to be the same length");
        }

        for c in bits_string.chars() {
            if c != '0'  && c != '1' && c != 'x' && c != 'X' {
                panic!("Illegal character in bits attribute");
            }
        }
    }

    // Can start generating code now
    // Dummy for now
    let bit_names = (0..num_bits).map(|x| LitStr::new(&x.to_string(), Span::call_site()));
    let bit_names2 = (0..num_bits).map(|x| LitStr::new(&x.to_string(), Span::call_site()));
    let bit_nums = 0..num_bits;

    // For encode function
    let encode_values = var_data.iter().map(|x|
        x.1.chars().map(|c|
            match c {
                '0'|'x' => quote! {false},
                '1'|'X' => quote! {true},
                _ => unreachable!(),
            }
        ).collect::<Vec<_>>()
    );
    let encode_var_id = var_data.iter().map(|x| x.0.clone());

    // For decode function
    let decode_values = var_data.iter().map(|x|
        x.1.chars().map(|c|
            match c {
                '0' => quote! {false},
                '1' => quote! {true},
                'x'|'X' => quote! {_},
                _ => unreachable!(),
            }
        ).collect::<Vec<_>>()
    );
    let decode_var_id = var_data.iter().map(|x| x.0.clone());

    let default_expr = default_expr.iter();
    let default_expr = if errtype.is_none() {
        quote!{
            #(,_ => Ok(#default_expr))*
        }
    } else {
        quote!{
            #(,_ => Err(#default_expr))*
        }
    };

    let errtype = if errtype.is_none() {
        quote!{()}
    } else {
        quote!{#errtype}
    };

    // For docs
    let variant_names = var_data.iter().map(|x| LitStr::new(&x.0.to_string(), Span::call_site()));
    let variant_docs = var_data.iter().map(|x| LitStr::new(&x.2.to_string(), Span::call_site()));
    let variant_bits = var_data.iter().map(|x| LitStr::new(&x.1.to_string(), Span::call_site()));

    let output = quote!{
        #input

        impl ::bittwiddler::BitPattern for #enum_id {
            type BitsArrType = [bool; #num_bits];
            const BITS_COUNT: usize = #num_bits;
            type ErrType = #errtype;

            type VarNamesIterType = ::std::slice::Iter<'static, &'static str>;
            type VarDescsIterType = ::std::slice::Iter<'static, &'static str>;
            type VarBitsIterType = ::std::slice::Iter<'static, &'static str>;

            fn encode(&self) -> Self::BitsArrType {
                match self {
                    #(Self::#encode_var_id => [#(#encode_values),*]),*
                }
            }

            fn decode(bits: Self::BitsArrType) -> Result<Self, Self::ErrType> {
                match bits {
                    #([#(#decode_values),*] => Ok(Self::#decode_var_id)),*
                    #default_expr
                }
            }

            fn _pos_to_name(pos: usize) -> &'static str {
                [#(#bit_names),*][pos]
            }

            fn _name_to_pos(name: &'static str) -> usize {
                match name {
                    #(#bit_names2 => #bit_nums),*
                    ,_ => unreachable!()
                }
            }

            fn variantnames() -> Self::VarNamesIterType {
                [#(#variant_names),*].iter()
            }

            fn variantdescs() -> Self::VarDescsIterType {
                [#(#variant_docs),*].iter()
            }

            fn variantbits() -> Self::VarBitsIterType {
                [#(#variant_bits),*].iter()
            }
        }
    };

    TokenStream::from(output)
}
<|endoftext|>


<commit_before>// #![feature(scoped)]
// #![feature(collections)]

extern crate mmap;
extern crate time;
extern crate timely;
extern crate columnar;
extern crate dataflow_join;

extern crate docopt;
use docopt::Docopt;

use std::thread;

use dataflow_join::graph::{GraphTrait, GraphMMap};

use timely::progress::timestamp::RootTimestamp;
use timely::progress::scope::Scope;
use timely::progress::nested::Summary::Local;
use timely::example_static::*;
use timely::communication::*;
use timely::communication::pact::Exchange;

use timely::networking::initialize_networking;
use timely::networking::initialize_networking_from_file;

use timely::drain::DrainExt;

static USAGE: &'static str = "
Usage: pagerank <source> [options] [<arguments>...]

Options:
    -w <arg>, --workers <arg>    number of workers per process [default: 1]
    -p <arg>, --processid <arg>  identity of this process      [default: 0]
    -n <arg>, --processes <arg>  number of processes involved  [default: 1]
    -h <arg>, --hosts <arg>      list of host:port for workers
";

fn main () {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());

    // let workers = if let Ok(threads) = args.get_str("<workers>").parse() { threads }
    //               else { panic!("invalid setting for workers: {}", args.get_str("<workers>")) };
    // println!("starting pagerank dataflow with {:?} worker{}", workers, if workers == 1 { "" } else { "s" });
    let source = args.get_str("<source>").to_owned();

    let workers: u64 = if let Ok(threads) = args.get_str("-w").parse() { threads }
                       else { panic!("invalid setting for --workers: {}", args.get_str("-t")) };
    let process_id: u64 = if let Ok(proc_id) = args.get_str("-p").parse() { proc_id }
                          else { panic!("invalid setting for --processid: {}", args.get_str("-p")) };
    let processes: u64 = if let Ok(processes) = args.get_str("-n").parse() { processes }
                         else { panic!("invalid setting for --processes: {}", args.get_str("-n")) };

    println!("Starting pagerank dataflow with");
    println!("\tworkers:\t{}", workers);
    println!("\tprocesses:\t{}", processes);
    println!("\tprocessid:\t{}", process_id);

    // vector holding communicators to use; one per local worker.
    if processes > 1 {
        println!("Initializing BinaryCommunicator");

        let hosts = args.get_str("-h");
        let communicators = if hosts != "" {
            initialize_networking_from_file(hosts, process_id, workers).ok().expect("error initializing networking")
        }
        else {
            let addresses = (0..processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
            initialize_networking(addresses, process_id, workers).ok().expect("error initializing networking")
        };

        pagerank_multi(communicators, source);
    }
    else if workers > 1 {
        println!("Initializing ProcessCommunicator");
        pagerank_multi(ProcessCommunicator::new_vector(workers), source);
    }
    else {
        println!("Initializing ThreadCommunicator");
        pagerank_multi(vec![ThreadCommunicator], source);
    };
}

fn pagerank_multi<C>(communicators: Vec<C>, filename: String)
where C: Communicator+Send {
    let mut guards = Vec::new();
    let workers = communicators.len();
    for communicator in communicators.into_iter() {
        let filename = filename.clone();
        guards.push(thread::Builder::new().name(format!("timely worker {}", communicator.index()))
                                          .spawn(move || pagerank(communicator, filename, workers))
                                          .unwrap());
    }

    for guard in guards { guard.join().unwrap(); }
}

fn pagerank<C>(communicator: C, filename: String, workers: usize)
where C: Communicator {
    let index = communicator.index() as usize;
    let peers = communicator.peers() as usize;

    let mut root = GraphRoot::new(communicator);

    {   // new scope avoids long borrow on root
        let mut builder = root.new_subgraph();

        // establish the beginnings of a loop,
        // 20 iterations, each time around += 1.
        let (helper, stream) = builder.loop_variable::<(u32, f32)>(RootTimestamp::new(20), Local(1));

        let graph = GraphMMap::<u32>::new(&filename);

        let nodes = graph.nodes();

        let mut src = vec![1.0; 1 + (nodes / peers as usize)];  // local rank accumulation
        let mut dst = vec![0.0; nodes];                         // local rank accumulation

        let mut start = time::precise_time_s();

        // from feedback, place an operator that
        // aggregates and broadcasts ranks along edges.
        let ranks = stream.enable(builder).unary_notify(

            Exchange::new(|x: &(u32, f32)| x.0 as u64),     // 1. how data should be exchanged
            format!("PageRank"),                            // 2. a tasteful, descriptive name
            vec![RootTimestamp::new(0)],                    // 3. indicate an initial capability
            move |input, output, iterator| {                // 4. provide the operator logic

                while let Some((iter, _)) = iterator.next() {

                    // /---- should look familiar! ----\
                    for node in 0..src.len() {
                        src[node] = 0.15 + 0.85 * src[node];
                    }

                    let mut counter = 0u64;
                    for node in 0..src.len() {
                        let edges = graph.edges(index + peers * node);
                        let value = src[node] / edges.len() as f32;
                        for &b in edges {
                            dst[b as usize] += value;
                        }
                    }
                    // \------ end familiar part ------/

                    output.give_at(&iter, dst.drain_temp()
                                             .enumerate()
                                             .filter(|&(_,f)| f != 0.0)
                                             .map(|(u,f)| (u as u32, f)));

                    for _ in 0..graph.nodes() { dst.push(0.0); }

                    println!("iteration {:?}: {}s", iter, time::precise_time_s() - start);
                    start = time::precise_time_s();
                }

                while let Some((iter, data)) = input.pull() {
                    iterator.notify_at(&iter);
                    for (node, rank) in data.drain_temp() {
                        src[node as usize / peers] += rank;
                    }
                }
            }
        );

        let local_index = index as usize % workers;

        let mut acc = vec![0.0; 1 + (nodes / workers)];

        ranks
        .unary_notify(
            Exchange::new(move |x: &(u32, f32)| (workers * (index / workers)) as u64 + (x.0 as u64 % workers as u64)),
            format!("Aggregation"),
            vec![],
            move |input, output, iterator| {
                while let Some((iter, data)) = input.pull() {
                    iterator.notify_at(&iter);
                    for (node, rank) in data.drain_temp() {
                        acc[node as usize / workers] += rank;
                    }
                }

                while let Some((item, _)) = iterator.next() {

                    output.give_at(&item, acc.drain_temp().enumerate().filter(|x| x.1 != 0.0)
                                             .map(|(u,f)| (((u * workers + local_index) as u32), f)));

                    for _ in 0..(nodes/workers) { acc.push(0.0); }
                    assert!(acc.len() == nodes/workers);
                }
            }
        )
        .connect_loop(helper);
    }

    while root.step() { }
}
<commit_msg>off by one in array allocation<commit_after>// #![feature(scoped)]
// #![feature(collections)]

extern crate mmap;
extern crate time;
extern crate timely;
extern crate columnar;
extern crate dataflow_join;

extern crate docopt;
use docopt::Docopt;

use std::thread;

use dataflow_join::graph::{GraphTrait, GraphMMap};

use timely::progress::timestamp::RootTimestamp;
use timely::progress::scope::Scope;
use timely::progress::nested::Summary::Local;
use timely::example_static::*;
use timely::communication::*;
use timely::communication::pact::Exchange;

use timely::networking::initialize_networking;
use timely::networking::initialize_networking_from_file;

use timely::drain::DrainExt;

static USAGE: &'static str = "
Usage: pagerank <source> [options] [<arguments>...]

Options:
    -w <arg>, --workers <arg>    number of workers per process [default: 1]
    -p <arg>, --processid <arg>  identity of this process      [default: 0]
    -n <arg>, --processes <arg>  number of processes involved  [default: 1]
    -h <arg>, --hosts <arg>      list of host:port for workers
";

fn main () {
    let args = Docopt::new(USAGE).and_then(|dopt| dopt.parse()).unwrap_or_else(|e| e.exit());

    // let workers = if let Ok(threads) = args.get_str("<workers>").parse() { threads }
    //               else { panic!("invalid setting for workers: {}", args.get_str("<workers>")) };
    // println!("starting pagerank dataflow with {:?} worker{}", workers, if workers == 1 { "" } else { "s" });
    let source = args.get_str("<source>").to_owned();

    let workers: u64 = if let Ok(threads) = args.get_str("-w").parse() { threads }
                       else { panic!("invalid setting for --workers: {}", args.get_str("-t")) };
    let process_id: u64 = if let Ok(proc_id) = args.get_str("-p").parse() { proc_id }
                          else { panic!("invalid setting for --processid: {}", args.get_str("-p")) };
    let processes: u64 = if let Ok(processes) = args.get_str("-n").parse() { processes }
                         else { panic!("invalid setting for --processes: {}", args.get_str("-n")) };

    println!("Starting pagerank dataflow with");
    println!("\tworkers:\t{}", workers);
    println!("\tprocesses:\t{}", processes);
    println!("\tprocessid:\t{}", process_id);

    // vector holding communicators to use; one per local worker.
    if processes > 1 {
        println!("Initializing BinaryCommunicator");

        let hosts = args.get_str("-h");
        let communicators = if hosts != "" {
            initialize_networking_from_file(hosts, process_id, workers).ok().expect("error initializing networking")
        }
        else {
            let addresses = (0..processes).map(|index| format!("localhost:{}", 2101 + index).to_string()).collect();
            initialize_networking(addresses, process_id, workers).ok().expect("error initializing networking")
        };

        pagerank_multi(communicators, source);
    }
    else if workers > 1 {
        println!("Initializing ProcessCommunicator");
        pagerank_multi(ProcessCommunicator::new_vector(workers), source);
    }
    else {
        println!("Initializing ThreadCommunicator");
        pagerank_multi(vec![ThreadCommunicator], source);
    };
}

fn pagerank_multi<C>(communicators: Vec<C>, filename: String)
where C: Communicator+Send {
    let mut guards = Vec::new();
    let workers = communicators.len();
    for communicator in communicators.into_iter() {
        let filename = filename.clone();
        guards.push(thread::Builder::new().name(format!("timely worker {}", communicator.index()))
                                          .spawn(move || pagerank(communicator, filename, workers))
                                          .unwrap());
    }

    for guard in guards { guard.join().unwrap(); }
}

fn pagerank<C>(communicator: C, filename: String, workers: usize)
where C: Communicator {
    let index = communicator.index() as usize;
    let peers = communicator.peers() as usize;

    let mut root = GraphRoot::new(communicator);

    {   // new scope avoids long borrow on root
        let mut builder = root.new_subgraph();

        // establish the beginnings of a loop,
        // 20 iterations, each time around += 1.
        let (helper, stream) = builder.loop_variable::<(u32, f32)>(RootTimestamp::new(20), Local(1));

        let graph = GraphMMap::<u32>::new(&filename);

        let nodes = graph.nodes();

        let mut src = vec![1.0; 1 + (nodes / peers as usize)];  // local rank accumulation
        let mut dst = vec![0.0; nodes];                         // local rank accumulation

        let mut start = time::precise_time_s();

        // from feedback, place an operator that
        // aggregates and broadcasts ranks along edges.
        let ranks = stream.enable(builder).unary_notify(

            Exchange::new(|x: &(u32, f32)| x.0 as u64),     // 1. how data should be exchanged
            format!("PageRank"),                            // 2. a tasteful, descriptive name
            vec![RootTimestamp::new(0)],                    // 3. indicate an initial capability
            move |input, output, iterator| {                // 4. provide the operator logic

                while let Some((iter, _)) = iterator.next() {

                    // /---- should look familiar! ----\
                    for node in 0..src.len() {
                        src[node] = 0.15 + 0.85 * src[node];
                    }

                    let mut counter = 0u64;
                    for node in 0..src.len() {
                        let edges = graph.edges(index + peers * node);
                        let value = src[node] / edges.len() as f32;
                        for &b in edges {
                            dst[b as usize] += value;
                        }
                    }
                    // \------ end familiar part ------/

                    output.give_at(&iter, dst.drain_temp()
                                             .enumerate()
                                             .filter(|&(_,f)| f != 0.0)
                                             .map(|(u,f)| (u as u32, f)));

                    for _ in 0..graph.nodes() { dst.push(0.0); }

                    println!("iteration {:?}: {}s", iter, time::precise_time_s() - start);
                    start = time::precise_time_s();
                }

                while let Some((iter, data)) = input.pull() {
                    iterator.notify_at(&iter);
                    for (node, rank) in data.drain_temp() {
                        src[node as usize / peers] += rank;
                    }
                }
            }
        );

        let local_index = index as usize % workers;

        let mut acc = vec![0.0; 1 + (nodes / workers)];

        ranks
        // .unary_notify(
        //     Exchange::new(move |x: &(u32, f32)| (workers * (index / workers)) as u64 + (x.0 as u64 % workers as u64)),
        //     format!("Aggregation"),
        //     vec![],
        //     move |input, output, iterator| {
        //         while let Some((iter, data)) = input.pull() {
        //             iterator.notify_at(&iter);
        //             for (node, rank) in data.drain_temp() {
        //                 acc[node as usize / workers] += rank;
        //             }
        //         }
        //
        //         while let Some((item, _)) = iterator.next() {
        //
        //             output.give_at(&item, acc.drain_temp().enumerate().filter(|x| x.1 != 0.0)
        //                                      .map(|(u,f)| (((u * workers + local_index) as u32), f)));
        //
        //             for _ in 0..(nodes/workers) { acc.push(0.0); }
        //             assert!(acc.len() == nodes/workers);
        //         }
        //     }
        // )
        .connect_loop(helper);
    }

    while root.step() { }
}
<|endoftext|>


<commit_before>use winapi::*;
use core::*;
use create_device::*;
use utils::*;
use dx_safe::*;
use structwrappers::*;
use std::ptr;

pub struct Downsampler {
  cpso: D3D12PipelineState,
  root_sig: D3D12RootSignature,
  dheap: D3D12DescriptorHeap,
  // descriptor handle increment size
  dsize: UINT,
  // luid of adapter cpso was generated on. generate_mips asserts that it is the same adapter as passed in DXCore
  luid: Luid,
}

impl Downsampler {
  pub fn new(dev: &D3D12Device) -> Downsampler {
    // create descriptor heap
    let c_hd=D3D12_DESCRIPTOR_HEAP_DESC{
      NumDescriptors: 2,
      Type: D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
      Flags: D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
      NodeMask: 0,
    };
    let dheap = dev.create_descriptor_heap(&c_hd).expect("Cannot create descriptor heap");

    let shader_bytecode =
      match d3d_compile_from_str(SHADER, "CSMain", "cs_5_1", 0) {
        Ok(sbc) => sbc,
        Err(err) => panic!("Downsampler shader compile error: {}", err),
      };
    let root_sig_bytecode =
      match d3d_compile_from_str(SHADER, "RSD", "rootsig_1_0", 0) {
        Ok(sbc) => sbc,
        Err(err) => panic!("Downsampler root signature compile error: {}", err),
      };
    debug!("Root signature");
    let root_sig = dev.create_root_signature(0, &root_sig_bytecode[..]).expect("Cannot create root signature");

    let srv_range = D3D12_DESCRIPTOR_RANGE {
      RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
      NumDescriptors: 1,
      BaseShaderRegister: 0,
      RegisterSpace: 0,
      OffsetInDescriptorsFromTableStart: 0,
    };

    let uav_range = D3D12_DESCRIPTOR_RANGE {
      RangeType: D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
      NumDescriptors: 1,
      BaseShaderRegister: 0,
      RegisterSpace: 0,
      OffsetInDescriptorsFromTableStart: 1,
    };

    let rs_parms = vec![
      *RootParameter::descriptor_table(&[srv_range], D3D12_SHADER_VISIBILITY_ALL),
      *RootParameter::descriptor_table(&[uav_range], D3D12_SHADER_VISIBILITY_ALL),
    ];

    let rsd=D3D12_ROOT_SIGNATURE_DESC{
      NumParameters: rs_parms.len() as UINT,
      pParameters: rs_parms[..].as_ptr(),
      NumStaticSamplers: 0,
      pStaticSamplers: ptr::null(),
      Flags: D3D12_ROOT_SIGNATURE_FLAGS(0),
    };

    debug!("Serialize root signature");
    let root_sig1_blob = d3d12_serialize_root_signature(&rsd, D3D_ROOT_SIGNATURE_VERSION_1).expect("cannot serialize root signature");

    debug!("Root signature1");
    let root_sig1 = dev.create_root_signature(0, blob_as_slice(&root_sig1_blob)).expect("Cannot create root signature");

    //It doesn't work.
    //let crs = d3d_get_root_signature_from_str(SHADER, dev).expect("Cannot extract root signature");
    let cpsd = D3D12_COMPUTE_PIPELINE_STATE_DESC {
      pRootSignature: ptr::null_mut(),
      CS: D3D12_SHADER_BYTECODE {
        pShaderBytecode: shader_bytecode.as_ptr() as *const _,
        BytecodeLength: shader_bytecode.len() as SIZE_T,
      },
      NodeMask: 0,
      CachedPSO: D3D12_CACHED_PIPELINE_STATE {pCachedBlob: ptr::null(), CachedBlobSizeInBytes: 0,},
      Flags: D3D12_PIPELINE_STATE_FLAG_NONE,
    };
    let cpso = dev.create_compute_pipeline_state(&cpsd).expect("Cannot create compute pipeline state");
    let dsize = dev.get_descriptor_handle_increment_size(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    Downsampler {
      cpso: cpso,
      root_sig: root_sig, //root_sig1 doesn't work.
      luid: Luid(dev.get_adapter_luid()),
      dheap: dheap,
      dsize: dsize,
    }
  }

  pub fn generate_mips(&self, tex: &D3D12Resource, core: &DXCore) -> HResult<()> {
    use std::sync::atomic::Ordering;

    trace!("generate_mips");
    trace!("Assert same adapter");
    assert!(&self.luid == &Luid(core.dev.get_adapter_luid()));

    trace!("rdesc");
    let rdesc = tex.get_desc();
    let (mut cw, mut ch) = ((rdesc.Width/2) as u32, rdesc.Height/2);

    // Create intermediate texture to hold computed mip-map levels
    // I had no success with generating mip-maps inplace.
    let tex_desc = D3D12_RESOURCE_DESC {
      MipLevels: 1,
      // D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS is required for generating mip-maps (at least it required for my method of generating mip-maps)
      .. resource_desc_tex2d_nomip(cw as u64, ch as u32, DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
    };
    trace!("Create intermediate texture");
    let im_tex = try!(core.dev.create_committed_resource(&heap_properties_default(), D3D12_HEAP_FLAG_NONE,
                              &tex_desc, D3D12_RESOURCE_STATE_COMMON, None));

    trace!("dh");
    let mut dh=self.dheap.get_cpu_descriptor_handle_for_heap_start();
    // advance handle to next descriptor
    dh.ptr += self.dsize as SIZE_T;
    // Descriptor for intermediate texture
    trace!("uav_desc");
    let uav_desc = uav_tex2d_desc(rdesc.Format, 0, 0);
    core.dev.create_unordered_access_view(Some(&im_tex), None, Some(&uav_desc), dh);

    // callocator and clist are destroyed on function exit.
    // It is not very efficient, but it's ok for now.
    trace!("callocator");
    let callocator = try!(core.dev.create_command_allocator(D3D12_COMMAND_LIST_TYPE_COMPUTE));
    // DX12 API developers possibly had an idea of type-level separation of graphics and compute command lists, but
    // they (wisely?) decided to keep interface count low and do runtime validation of commands.
    // So, compute and graphics command lists are represented as D3D12GraphicsCommandList.
    trace!("clist");
    let clist: D3D12GraphicsCommandList = try!(core.dev.create_command_list(0, D3D12_COMMAND_LIST_TYPE_COMPUTE, &callocator, Some(&self.cpso)));
    try!(clist.close());

    let fence = try!(core.dev.create_fence(0, D3D12_FENCE_FLAG_NONE));
    let fence_event = create_event();

    for i in 0..((rdesc.MipLevels-1) as u32) {
      try!(callocator.reset());
      try!(clist.reset(&callocator, None));
      // Descriptor handle
      trace!("dh");
      let mut dh=self.dheap.get_cpu_descriptor_handle_for_heap_start();
      // Create descriptors in descriptor heap.
      // Their layout should match root signature specified in compute shader below.
      // Descriptor for source mip-map slice
      trace!("srv_desc");
      let srv_desc = srv_tex2d_default_slice_mip(rdesc.Format, i, 1);
      core.dev.create_shader_resource_view(Some(&tex), Some(&srv_desc), dh);

      trace!("clist.set_pipeline_state");
      clist.set_pipeline_state(&self.cpso);
      clist.set_compute_root_signature(&self.root_sig);
      trace!("clist.set_descriptor_heaps");
      clist.set_descriptor_heaps(&[&self.dheap]);
      let mut gdh = self.dheap.get_gpu_descriptor_handle_for_heap_start();
      trace!("clist.set_compute_root_descriptor_table(0, ...");
      clist.set_compute_root_descriptor_table(0, gdh);
      //gdh.ptr += self.dsize as SIZE_T;
      //debug!("clist.set_compute_root_descriptor_table(1, ...");
      //clist.set_compute_root_descriptor_table(1, gdh);

      trace!("clist.resource_barrier");
      clist.resource_barrier(&[
          // Transition source mip-map slice
          *ResourceBarrier::transition_subresource(&tex, i,
            D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE),
          // Transition intermediate texture
          *ResourceBarrier::transition(&im_tex, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS),
        ]);
      clist.dispatch(cw, ch, 1);
      clist.resource_barrier(&[
          // Transition source mip-map slice
          *ResourceBarrier::transition_subresource(&tex, i,
            D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COMMON),
          // Transition intermediate texture
          *ResourceBarrier::transition(&im_tex,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE),
          // Transition target slice
          *ResourceBarrier::transition_subresource(&tex, i+1,
            D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST),
        ]);

      let source_copy_loc = texture_copy_location_index(&im_tex, 0);
      let dest_copy_loc = texture_copy_location_index(&tex, i+1);

      let src_box = D3D12_BOX {
        left: 0, top: 0, front: 0,
        right: cw, bottom: ch, back: 1,
      };
      clist.copy_texture_region(&dest_copy_loc, 0, 0, 0, &source_copy_loc, Some(&src_box));
      clist.resource_barrier(&[
          // Transition intermediate texture
          *ResourceBarrier::transition(&im_tex,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON),
          // Transition target slice
          *ResourceBarrier::transition_subresource(&tex, i+1,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON),
        ]);

      // I forgot to close command list. So I close it now
      try!(clist.close().map_err(|hr|{dump_info_queue(core.info_queue.as_ref()); hr}));

      core.compute_queue.execute_command_lists(&[&clist]);

      let fence_value = core.fence_value.fetch_add(1, Ordering::Relaxed) as u64;
      try!(wait_for_queue(&core.compute_queue, fence_value, &fence, &fence_event));

      cw /= 2;
      ch /= 2;
    };
    dump_info_queue(core.info_queue.as_ref());

    Ok(())
  }
}

static SHADER: &'static str = r#"
#define RSD "RootFlags(0), DescriptorTable(SRV(t0), UAV(u0))" // 2

Texture2D<float4> mip0: register(t0);//4
RWTexture2D<float4> mip1: register(u0);//5

static const float coefs[4] = {1./8., 3./8., 3./8., 1./8.};//7
groupshared float4 color_accum[4][4];//8

[RootSignature(RSD)]//10
[numthreads(4, 4, 1)]//11
void CSMain(uint3 gtid: SV_GroupThreadId, uint3 gid: SV_GroupId) {//12
  int w=0;
  int h=0;
  mip0.GetDimensions(w, h);
  float c = coefs[gtid.x]*coefs[gtid.y];//13
  int2 idx = clamp(gid.xy*2 + gtid.xy + int2(-1,-1), int2(0,0), int2(w-1,h-1));

  color_accum[gtid.x][gtid.y] = mip0[idx]*c;//14
  GroupMemoryBarrierWithGroupSync();//15
  if (gtid.x==1 && gtid.y==1) {//16
    float4 a=float4(0,0,0,0);//17
    for(uint i=0; i<4; ++i) {
      for(uint j=0; j<4; ++j) {
         a += color_accum[i][j];
      };
    };
    mip1[gid.xy] = a;//float4(1, 1, 1, 1);//a;
  };
}
"#;

<commit_msg>root_sig1 removed<commit_after>use winapi::*;
use core::*;
use create_device::*;
use utils::*;
use dx_safe::*;
use structwrappers::*;
use std::ptr;

pub struct Downsampler {
  cpso: D3D12PipelineState,
  root_sig: D3D12RootSignature,
  dheap: D3D12DescriptorHeap,
  // descriptor handle increment size
  dsize: UINT,
  // luid of adapter cpso was generated on. generate_mips asserts that it is the same adapter as passed in DXCore
  luid: Luid,
}

impl Downsampler {
  pub fn new(dev: &D3D12Device) -> Downsampler {
    // create descriptor heap
    let c_hd=D3D12_DESCRIPTOR_HEAP_DESC{
      NumDescriptors: 2,
      Type: D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
      Flags: D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
      NodeMask: 0,
    };
    let dheap = dev.create_descriptor_heap(&c_hd).expect("Cannot create descriptor heap");

    let shader_bytecode =
      match d3d_compile_from_str(SHADER, "CSMain", "cs_5_1", 0) {
        Ok(sbc) => sbc,
        Err(err) => panic!("Downsampler shader compile error: {}", err),
      };
    let root_sig_bytecode =
      match d3d_compile_from_str(SHADER, "RSD", "rootsig_1_0", 0) {
        Ok(sbc) => sbc,
        Err(err) => panic!("Downsampler root signature compile error: {}", err),
      };
    debug!("Root signature");
    let root_sig = dev.create_root_signature(0, &root_sig_bytecode[..]).expect("Cannot create root signature");

    //It doesn't work.
    //let crs = d3d_get_root_signature_from_str(SHADER, dev).expect("Cannot extract root signature");
    let cpsd = D3D12_COMPUTE_PIPELINE_STATE_DESC {
      pRootSignature: ptr::null_mut(),
      CS: D3D12_SHADER_BYTECODE {
        pShaderBytecode: shader_bytecode.as_ptr() as *const _,
        BytecodeLength: shader_bytecode.len() as SIZE_T,
      },
      NodeMask: 0,
      CachedPSO: D3D12_CACHED_PIPELINE_STATE {pCachedBlob: ptr::null(), CachedBlobSizeInBytes: 0,},
      Flags: D3D12_PIPELINE_STATE_FLAG_NONE,
    };
    let cpso = dev.create_compute_pipeline_state(&cpsd).expect("Cannot create compute pipeline state");
    let dsize = dev.get_descriptor_handle_increment_size(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    Downsampler {
      cpso: cpso,
      root_sig: root_sig,
      luid: Luid(dev.get_adapter_luid()),
      dheap: dheap,
      dsize: dsize,
    }
  }

  pub fn generate_mips(&self, tex: &D3D12Resource, core: &DXCore) -> HResult<()> {
    use std::sync::atomic::Ordering;

    trace!("generate_mips");
    trace!("Assert same adapter");
    assert!(&self.luid == &Luid(core.dev.get_adapter_luid()));

    trace!("rdesc");
    let rdesc = tex.get_desc();
    let (mut cw, mut ch) = ((rdesc.Width/2) as u32, rdesc.Height/2);

    // Create intermediate texture to hold computed mip-map levels
    // I had no success with generating mip-maps inplace.
    let tex_desc = D3D12_RESOURCE_DESC {
      MipLevels: 1,
      // D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS is required for generating mip-maps (at least it required for my method of generating mip-maps)
      .. resource_desc_tex2d_nomip(cw as u64, ch as u32, DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
    };
    trace!("Create intermediate texture");
    let im_tex = try!(core.dev.create_committed_resource(&heap_properties_default(), D3D12_HEAP_FLAG_NONE,
                              &tex_desc, D3D12_RESOURCE_STATE_COMMON, None));

    trace!("dh");
    let mut dh=self.dheap.get_cpu_descriptor_handle_for_heap_start();
    // advance handle to next descriptor
    dh.ptr += self.dsize as SIZE_T;
    // Descriptor for intermediate texture
    trace!("uav_desc");
    let uav_desc = uav_tex2d_desc(rdesc.Format, 0, 0);
    core.dev.create_unordered_access_view(Some(&im_tex), None, Some(&uav_desc), dh);

    // callocator and clist are destroyed on function exit.
    // It is not very efficient, but it's ok for now.
    trace!("callocator");
    let callocator = try!(core.dev.create_command_allocator(D3D12_COMMAND_LIST_TYPE_COMPUTE));
    // DX12 API developers possibly had an idea of type-level separation of graphics and compute command lists, but
    // they (wisely?) decided to keep interface count low and do runtime validation of commands.
    // So, compute and graphics command lists are represented as D3D12GraphicsCommandList.
    trace!("clist");
    let clist: D3D12GraphicsCommandList = try!(core.dev.create_command_list(0, D3D12_COMMAND_LIST_TYPE_COMPUTE, &callocator, Some(&self.cpso)));
    try!(clist.close());

    let fence = try!(core.dev.create_fence(0, D3D12_FENCE_FLAG_NONE));
    let fence_event = create_event();

    for i in 0..((rdesc.MipLevels-1) as u32) {
      try!(callocator.reset());
      try!(clist.reset(&callocator, None));
      // Descriptor handle
      trace!("dh");
      let mut dh=self.dheap.get_cpu_descriptor_handle_for_heap_start();
      // Create descriptors in descriptor heap.
      // Their layout should match root signature specified in compute shader below.
      // Descriptor for source mip-map slice
      trace!("srv_desc");
      let srv_desc = srv_tex2d_default_slice_mip(rdesc.Format, i, 1);
      core.dev.create_shader_resource_view(Some(&tex), Some(&srv_desc), dh);

      trace!("clist.set_pipeline_state");
      clist.set_pipeline_state(&self.cpso);
      clist.set_compute_root_signature(&self.root_sig);
      trace!("clist.set_descriptor_heaps");
      clist.set_descriptor_heaps(&[&self.dheap]);
      let mut gdh = self.dheap.get_gpu_descriptor_handle_for_heap_start();
      trace!("clist.set_compute_root_descriptor_table(0, ...");
      clist.set_compute_root_descriptor_table(0, gdh);
      //gdh.ptr += self.dsize as SIZE_T;
      //debug!("clist.set_compute_root_descriptor_table(1, ...");
      //clist.set_compute_root_descriptor_table(1, gdh);

      trace!("clist.resource_barrier");
      clist.resource_barrier(&[
          // Transition source mip-map slice
          *ResourceBarrier::transition_subresource(&tex, i,
            D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE),
          // Transition intermediate texture
          *ResourceBarrier::transition(&im_tex, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS),
        ]);
      clist.dispatch(cw, ch, 1);
      clist.resource_barrier(&[
          // Transition source mip-map slice
          *ResourceBarrier::transition_subresource(&tex, i,
            D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COMMON),
          // Transition intermediate texture
          *ResourceBarrier::transition(&im_tex,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE),
          // Transition target slice
          *ResourceBarrier::transition_subresource(&tex, i+1,
            D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST),
        ]);

      let source_copy_loc = texture_copy_location_index(&im_tex, 0);
      let dest_copy_loc = texture_copy_location_index(&tex, i+1);

      let src_box = D3D12_BOX {
        left: 0, top: 0, front: 0,
        right: cw, bottom: ch, back: 1,
      };
      clist.copy_texture_region(&dest_copy_loc, 0, 0, 0, &source_copy_loc, Some(&src_box));
      clist.resource_barrier(&[
          // Transition intermediate texture
          *ResourceBarrier::transition(&im_tex,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON),
          // Transition target slice
          *ResourceBarrier::transition_subresource(&tex, i+1,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON),
        ]);

      // I forgot to close command list. So I close it now
      try!(clist.close().map_err(|hr|{dump_info_queue(core.info_queue.as_ref()); hr}));

      core.compute_queue.execute_command_lists(&[&clist]);

      let fence_value = core.fence_value.fetch_add(1, Ordering::Relaxed) as u64;
      try!(wait_for_queue(&core.compute_queue, fence_value, &fence, &fence_event));

      cw /= 2;
      ch /= 2;
    };
    dump_info_queue(core.info_queue.as_ref());

    Ok(())
  }
}

static SHADER: &'static str = r#"
#define RSD "RootFlags(0), DescriptorTable(SRV(t0), UAV(u0))" // 2

Texture2D<float4> mip0: register(t0);//4
RWTexture2D<float4> mip1: register(u0);//5

static const float coefs[4] = {1./8., 3./8., 3./8., 1./8.};//7
groupshared float4 color_accum[4][4];//8

[RootSignature(RSD)]//10
[numthreads(4, 4, 1)]//11
void CSMain(uint3 gtid: SV_GroupThreadId, uint3 gid: SV_GroupId) {//12
  int w=0;
  int h=0;
  mip0.GetDimensions(w, h);
  float c = coefs[gtid.x]*coefs[gtid.y];//13
  int2 idx = clamp(gid.xy*2 + gtid.xy + int2(-1,-1), int2(0,0), int2(w-1,h-1));

  color_accum[gtid.x][gtid.y] = mip0[idx]*c;//14
  GroupMemoryBarrierWithGroupSync();//15
  if (gtid.x==1 && gtid.y==1) {//16
    float4 a=float4(0,0,0,0);//17
    for(uint i=0; i<4; ++i) {
      for(uint j=0; j<4; ++j) {
         a += color_accum[i][j];
      };
    };
    mip1[gid.xy] = a;//float4(1, 1, 1, 1);//a;
  };
}
"#;

<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// NB: transitionary, de-mode-ing.
#[forbid(deprecated_mode)];
#[forbid(deprecated_pattern)];

use T = self::inst::T;

use char;
use cmp::{Eq, Ord};
use cmp;
use from_str::FromStr;
use iter;
use num;
use num::Num::from_int;
use prelude::*;
use str;
use uint;
use vec;

pub const bits : uint = inst::bits;
pub const bytes : uint = (inst::bits / 8);

pub const min_value: T = (-1 as T) << (bits - 1);
pub const max_value: T = min_value - 1 as T;

#[inline(always)]
pub pure fn min(x: T, y: T) -> T { if x < y { x } else { y } }
#[inline(always)]
pub pure fn max(x: T, y: T) -> T { if x > y { x } else { y } }

#[inline(always)]
pub pure fn add(x: T, y: T) -> T { x + y }
#[inline(always)]
pub pure fn sub(x: T, y: T) -> T { x - y }
#[inline(always)]
pub pure fn mul(x: T, y: T) -> T { x * y }
#[inline(always)]
pub pure fn div(x: T, y: T) -> T { x / y }
#[inline(always)]
pub pure fn rem(x: T, y: T) -> T { x % y }

#[inline(always)]
pub pure fn lt(x: T, y: T) -> bool { x < y }
#[inline(always)]
pub pure fn le(x: T, y: T) -> bool { x <= y }
#[inline(always)]
pub pure fn eq(x: T, y: T) -> bool { x == y }
#[inline(always)]
pub pure fn ne(x: T, y: T) -> bool { x != y }
#[inline(always)]
pub pure fn ge(x: T, y: T) -> bool { x >= y }
#[inline(always)]
pub pure fn gt(x: T, y: T) -> bool { x > y }

#[inline(always)]
pub pure fn is_positive(x: T) -> bool { x > 0 as T }
#[inline(always)]
pub pure fn is_negative(x: T) -> bool { x < 0 as T }
#[inline(always)]
pub pure fn is_nonpositive(x: T) -> bool { x <= 0 as T }
#[inline(always)]
pub pure fn is_nonnegative(x: T) -> bool { x >= 0 as T }

#[inline(always)]
/// Iterate over the range [`lo`..`hi`)
pub fn range(lo: T, hi: T, it: fn(T) -> bool) {
    let mut i = lo;
    while i < hi {
        if !it(i) { break }
        i += 1 as T;
    }
}

/// Computes the bitwise complement
#[inline(always)]
pub pure fn compl(i: T) -> T {
    -1 as T ^ i
}

/// Computes the absolute value
#[inline(always)]
pub pure fn abs(i: T) -> T {
    if is_negative(i) { -i } else { i }
}

#[cfg(notest)]
impl T : Ord {
    #[inline(always)]
    pure fn lt(&self, other: &T) -> bool { return (*self) < (*other); }
    #[inline(always)]
    pure fn le(&self, other: &T) -> bool { return (*self) <= (*other); }
    #[inline(always)]
    pure fn ge(&self, other: &T) -> bool { return (*self) >= (*other); }
    #[inline(always)]
    pure fn gt(&self, other: &T) -> bool { return (*self) > (*other); }
}

#[cfg(notest)]
impl T : Eq {
    #[inline(always)]
    pure fn eq(&self, other: &T) -> bool { return (*self) == (*other); }
    #[inline(always)]
    pure fn ne(&self, other: &T) -> bool { return (*self) != (*other); }
}

impl T: num::Num {
    #[inline(always)]
    pure fn add(&self, other: &T)    -> T { return *self + *other; }
    #[inline(always)]
    pure fn sub(&self, other: &T)    -> T { return *self - *other; }
    #[inline(always)]
    pure fn mul(&self, other: &T)    -> T { return *self * *other; }
    #[inline(always)]
    pure fn div(&self, other: &T)    -> T { return *self / *other; }
    #[inline(always)]
    pure fn modulo(&self, other: &T) -> T { return *self % *other; }
    #[inline(always)]
    pure fn neg(&self)              -> T { return -*self;        }

    #[inline(always)]
    pure fn to_int(&self)         -> int { return *self as int; }
    #[inline(always)]
    static pure fn from_int(n: int) -> T   { return n as T;      }
}

impl T: num::Zero {
    #[inline(always)]
    static pure fn zero() -> T { 0 }
}

impl T: num::One {
    #[inline(always)]
    static pure fn one() -> T { 1 }
}

impl T: iter::Times {
    #[inline(always)]
    #[doc = "A convenience form for basic iteration. Given a variable `x` \
        of any numeric type, the expression `for x.times { /* anything */ }` \
        will execute the given function exactly x times. If we assume that \
        `x` is an int, this is functionally equivalent to \
        `for int::range(0, x) |_i| { /* anything */ }`."]
    pure fn times(&self, it: fn() -> bool) {
        if *self < 0 {
            fail fmt!("The .times method expects a nonnegative number, \
                       but found %?", self);
        }
        let mut i = *self;
        while i > 0 {
            if !it() { break }
            i -= 1;
        }
    }
}

/**
 * Parse a buffer of bytes
 *
 * # Arguments
 *
 * * buf - A byte buffer
 * * radix - The base of the number
 */
pub pure fn parse_bytes(buf: &[u8], radix: uint) -> Option<T> {
    if vec::len(buf) == 0u { return None; }
    let mut i = vec::len(buf) - 1u;
    let mut start = 0u;
    let mut power = 1 as T;

    if buf[0] == ('-' as u8) {
        power = -1 as T;
        start = 1u;
    }
    let mut n = 0 as T;
    loop {
        match char::to_digit(buf[i] as char, radix) {
          Some(d) => n += (d as T) * power,
          None => return None
        }
        power *= radix as T;
        if i <= start { return Some(n); }
        i -= 1u;
    };
}

/// Parse a string to an int
#[inline(always)]
pub pure fn from_str(s: &str) -> Option<T>
{
    parse_bytes(str::to_bytes(s), 10u)
}

impl T : FromStr {
    #[inline(always)]
    static pure fn from_str(s: &str) -> Option<T> { from_str(s) }
}

/// Convert to a string in a given base
#[inline(always)]
pub pure fn to_str(n: T, radix: uint) -> ~str {
    do to_str_bytes(n, radix) |slice| {
        do vec::as_imm_buf(slice) |p, len| {
            unsafe { str::raw::from_buf_len(p, len) }
        }
    }
}

#[inline(always)]
pub pure fn to_str_bytes<U>(n: T, radix: uint, f: fn(v: &[u8]) -> U) -> U {
    if n < 0 as T {
        uint::to_str_bytes(true, -n as uint, radix, f)
    } else {
        uint::to_str_bytes(false, n as uint, radix, f)
    }
}

/// Convert to a string
#[inline(always)]
pub pure fn str(i: T) -> ~str { return to_str(i, 10u); }

#[test]
fn test_from_str() {
    assert from_str(~"0") == Some(0 as T);
    assert from_str(~"3") == Some(3 as T);
    assert from_str(~"10") == Some(10 as T);
    assert from_str(~"123456789") == Some(123456789 as T);
    assert from_str(~"00100") == Some(100 as T);

    assert from_str(~"-1") == Some(-1 as T);
    assert from_str(~"-3") == Some(-3 as T);
    assert from_str(~"-10") == Some(-10 as T);
    assert from_str(~"-123456789") == Some(-123456789 as T);
    assert from_str(~"-00100") == Some(-100 as T);

    assert from_str(~" ").is_none();
    assert from_str(~"x").is_none();
}

#[test]
fn test_parse_bytes() {
    use str::to_bytes;
    assert parse_bytes(to_bytes(~"123"), 10u) == Some(123 as T);
    assert parse_bytes(to_bytes(~"1001"), 2u) == Some(9 as T);
    assert parse_bytes(to_bytes(~"123"), 8u) == Some(83 as T);
    assert parse_bytes(to_bytes(~"123"), 16u) == Some(291 as T);
    assert parse_bytes(to_bytes(~"ffff"), 16u) == Some(65535 as T);
    assert parse_bytes(to_bytes(~"FFFF"), 16u) == Some(65535 as T);
    assert parse_bytes(to_bytes(~"z"), 36u) == Some(35 as T);
    assert parse_bytes(to_bytes(~"Z"), 36u) == Some(35 as T);

    assert parse_bytes(to_bytes(~"-123"), 10u) == Some(-123 as T);
    assert parse_bytes(to_bytes(~"-1001"), 2u) == Some(-9 as T);
    assert parse_bytes(to_bytes(~"-123"), 8u) == Some(-83 as T);
    assert parse_bytes(to_bytes(~"-123"), 16u) == Some(-291 as T);
    assert parse_bytes(to_bytes(~"-ffff"), 16u) == Some(-65535 as T);
    assert parse_bytes(to_bytes(~"-FFFF"), 16u) == Some(-65535 as T);
    assert parse_bytes(to_bytes(~"-z"), 36u) == Some(-35 as T);
    assert parse_bytes(to_bytes(~"-Z"), 36u) == Some(-35 as T);

    assert parse_bytes(to_bytes(~"Z"), 35u).is_none();
    assert parse_bytes(to_bytes(~"-9"), 2u).is_none();
}

#[test]
fn test_to_str() {
    assert (to_str(0 as T, 10u) == ~"0");
    assert (to_str(1 as T, 10u) == ~"1");
    assert (to_str(-1 as T, 10u) == ~"-1");
    assert (to_str(127 as T, 16u) == ~"7f");
    assert (to_str(100 as T, 10u) == ~"100");
}

#[test]
fn test_interfaces() {
    fn test<U:num::Num cmp::Eq>(ten: U) {
        assert (ten.to_int() == 10);

        let two: U = from_int(2);
        assert (two.to_int() == 2);

        assert (ten.add(&two) == from_int(12));
        assert (ten.sub(&two) == from_int(8));
        assert (ten.mul(&two) == from_int(20));
        assert (ten.div(&two) == from_int(5));
        assert (ten.modulo(&two) == from_int(0));
        assert (ten.neg() == from_int(-10));
    }

    test(10 as T);
}

#[test]
fn test_times() {
    use iter::Times;
    let ten = 10 as T;
    let mut accum = 0;
    for ten.times { accum += 1; }
    assert (accum == 10);
}

#[test]
#[should_fail]
#[ignore(cfg(windows))]
fn test_times_negative() {
    use iter::Times;
    for (-10).times { log(error, ~"nope!"); }
}
<commit_msg>minor int-template.rs cleanup<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// NB: transitionary, de-mode-ing.
#[forbid(deprecated_mode)];
#[forbid(deprecated_pattern)];

use T = self::inst::T;

use char;
use cmp::{Eq, Ord};
use cmp;
use from_str::FromStr;
use iter;
use num;
use num::Num::from_int;
use prelude::*;
use str;
use uint;
use vec;

pub const bits : uint = inst::bits;
pub const bytes : uint = (inst::bits / 8);

pub const min_value: T = (-1 as T) << (bits - 1);
pub const max_value: T = min_value - 1 as T;

#[inline(always)]
pub pure fn min(x: T, y: T) -> T { if x < y { x } else { y } }
#[inline(always)]
pub pure fn max(x: T, y: T) -> T { if x > y { x } else { y } }

#[inline(always)]
pub pure fn add(x: T, y: T) -> T { x + y }
#[inline(always)]
pub pure fn sub(x: T, y: T) -> T { x - y }
#[inline(always)]
pub pure fn mul(x: T, y: T) -> T { x * y }
#[inline(always)]
pub pure fn div(x: T, y: T) -> T { x / y }
#[inline(always)]
pub pure fn rem(x: T, y: T) -> T { x % y }

#[inline(always)]
pub pure fn lt(x: T, y: T) -> bool { x < y }
#[inline(always)]
pub pure fn le(x: T, y: T) -> bool { x <= y }
#[inline(always)]
pub pure fn eq(x: T, y: T) -> bool { x == y }
#[inline(always)]
pub pure fn ne(x: T, y: T) -> bool { x != y }
#[inline(always)]
pub pure fn ge(x: T, y: T) -> bool { x >= y }
#[inline(always)]
pub pure fn gt(x: T, y: T) -> bool { x > y }

#[inline(always)]
pub pure fn is_positive(x: T) -> bool { x > 0 as T }
#[inline(always)]
pub pure fn is_negative(x: T) -> bool { x < 0 as T }
#[inline(always)]
pub pure fn is_nonpositive(x: T) -> bool { x <= 0 as T }
#[inline(always)]
pub pure fn is_nonnegative(x: T) -> bool { x >= 0 as T }

#[inline(always)]
/// Iterate over the range [`lo`..`hi`)
pub pure fn range(lo: T, hi: T, it: fn(T) -> bool) {
    let mut i = lo;
    while i < hi {
        if !it(i) { break }
        i += 1 as T;
    }
}

/// Computes the bitwise complement
#[inline(always)]
pub pure fn compl(i: T) -> T {
    -1 as T ^ i
}

/// Computes the absolute value
#[inline(always)]
pub pure fn abs(i: T) -> T {
    if is_negative(i) { -i } else { i }
}

#[cfg(notest)]
impl T : Ord {
    #[inline(always)]
    pure fn lt(&self, other: &T) -> bool { return (*self) < (*other); }
    #[inline(always)]
    pure fn le(&self, other: &T) -> bool { return (*self) <= (*other); }
    #[inline(always)]
    pure fn ge(&self, other: &T) -> bool { return (*self) >= (*other); }
    #[inline(always)]
    pure fn gt(&self, other: &T) -> bool { return (*self) > (*other); }
}

#[cfg(notest)]
impl T : Eq {
    #[inline(always)]
    pure fn eq(&self, other: &T) -> bool { return (*self) == (*other); }
    #[inline(always)]
    pure fn ne(&self, other: &T) -> bool { return (*self) != (*other); }
}

impl T: num::Num {
    #[inline(always)]
    pure fn add(&self, other: &T)    -> T { return *self + *other; }
    #[inline(always)]
    pure fn sub(&self, other: &T)    -> T { return *self - *other; }
    #[inline(always)]
    pure fn mul(&self, other: &T)    -> T { return *self * *other; }
    #[inline(always)]
    pure fn div(&self, other: &T)    -> T { return *self / *other; }
    #[inline(always)]
    pure fn modulo(&self, other: &T) -> T { return *self % *other; }
    #[inline(always)]
    pure fn neg(&self)              -> T { return -*self;        }

    #[inline(always)]
    pure fn to_int(&self)         -> int { return *self as int; }
    #[inline(always)]
    static pure fn from_int(n: int) -> T   { return n as T;      }
}

impl T: num::Zero {
    #[inline(always)]
    static pure fn zero() -> T { 0 }
}

impl T: num::One {
    #[inline(always)]
    static pure fn one() -> T { 1 }
}

impl T: iter::Times {
    #[inline(always)]
    #[doc = "A convenience form for basic iteration. Given a variable `x` \
        of any numeric type, the expression `for x.times { /* anything */ }` \
        will execute the given function exactly x times. If we assume that \
        `x` is an int, this is functionally equivalent to \
        `for int::range(0, x) |_i| { /* anything */ }`."]
    pure fn times(&self, it: fn() -> bool) {
        if is_negative(*self) {
            fail fmt!("The .times method expects a nonnegative number, \
                       but found %?", self);
        }
        let mut i = *self;
        while i > 0 {
            if !it() { break }
            i -= 1;
        }
    }
}

/**
 * Parse a buffer of bytes
 *
 * # Arguments
 *
 * * buf - A byte buffer
 * * radix - The base of the number
 */
pub pure fn parse_bytes(buf: &[u8], radix: uint) -> Option<T> {
    if vec::len(buf) == 0u { return None; }
    let mut i = vec::len(buf) - 1u;
    let mut start = 0u;
    let mut power = 1 as T;

    if buf[0] == ('-' as u8) {
        power = -1 as T;
        start = 1u;
    }
    let mut n = 0 as T;
    loop {
        match char::to_digit(buf[i] as char, radix) {
          Some(d) => n += (d as T) * power,
          None => return None
        }
        power *= radix as T;
        if i <= start { return Some(n); }
        i -= 1u;
    };
}

/// Parse a string to an int
#[inline(always)]
pub pure fn from_str(s: &str) -> Option<T>
{
    parse_bytes(str::to_bytes(s), 10u)
}

impl T : FromStr {
    #[inline(always)]
    static pure fn from_str(s: &str) -> Option<T> { from_str(s) }
}

/// Convert to a string in a given base
#[inline(always)]
pub pure fn to_str(n: T, radix: uint) -> ~str {
    do to_str_bytes(n, radix) |slice| {
        do vec::as_imm_buf(slice) |p, len| {
            unsafe { str::raw::from_buf_len(p, len) }
        }
    }
}

#[inline(always)]
pub pure fn to_str_bytes<U>(n: T, radix: uint, f: fn(v: &[u8]) -> U) -> U {
    if n < 0 as T {
        uint::to_str_bytes(true, -n as uint, radix, f)
    } else {
        uint::to_str_bytes(false, n as uint, radix, f)
    }
}

/// Convert to a string
#[inline(always)]
pub pure fn str(i: T) -> ~str { return to_str(i, 10u); }

#[test]
fn test_from_str() {
    assert from_str(~"0") == Some(0 as T);
    assert from_str(~"3") == Some(3 as T);
    assert from_str(~"10") == Some(10 as T);
    assert from_str(~"123456789") == Some(123456789 as T);
    assert from_str(~"00100") == Some(100 as T);

    assert from_str(~"-1") == Some(-1 as T);
    assert from_str(~"-3") == Some(-3 as T);
    assert from_str(~"-10") == Some(-10 as T);
    assert from_str(~"-123456789") == Some(-123456789 as T);
    assert from_str(~"-00100") == Some(-100 as T);

    assert from_str(~" ").is_none();
    assert from_str(~"x").is_none();
}

#[test]
fn test_parse_bytes() {
    use str::to_bytes;
    assert parse_bytes(to_bytes(~"123"), 10u) == Some(123 as T);
    assert parse_bytes(to_bytes(~"1001"), 2u) == Some(9 as T);
    assert parse_bytes(to_bytes(~"123"), 8u) == Some(83 as T);
    assert parse_bytes(to_bytes(~"123"), 16u) == Some(291 as T);
    assert parse_bytes(to_bytes(~"ffff"), 16u) == Some(65535 as T);
    assert parse_bytes(to_bytes(~"FFFF"), 16u) == Some(65535 as T);
    assert parse_bytes(to_bytes(~"z"), 36u) == Some(35 as T);
    assert parse_bytes(to_bytes(~"Z"), 36u) == Some(35 as T);

    assert parse_bytes(to_bytes(~"-123"), 10u) == Some(-123 as T);
    assert parse_bytes(to_bytes(~"-1001"), 2u) == Some(-9 as T);
    assert parse_bytes(to_bytes(~"-123"), 8u) == Some(-83 as T);
    assert parse_bytes(to_bytes(~"-123"), 16u) == Some(-291 as T);
    assert parse_bytes(to_bytes(~"-ffff"), 16u) == Some(-65535 as T);
    assert parse_bytes(to_bytes(~"-FFFF"), 16u) == Some(-65535 as T);
    assert parse_bytes(to_bytes(~"-z"), 36u) == Some(-35 as T);
    assert parse_bytes(to_bytes(~"-Z"), 36u) == Some(-35 as T);

    assert parse_bytes(to_bytes(~"Z"), 35u).is_none();
    assert parse_bytes(to_bytes(~"-9"), 2u).is_none();
}

#[test]
fn test_to_str() {
    assert (to_str(0 as T, 10u) == ~"0");
    assert (to_str(1 as T, 10u) == ~"1");
    assert (to_str(-1 as T, 10u) == ~"-1");
    assert (to_str(127 as T, 16u) == ~"7f");
    assert (to_str(100 as T, 10u) == ~"100");
}

#[test]
fn test_interfaces() {
    fn test<U:num::Num cmp::Eq>(ten: U) {
        assert (ten.to_int() == 10);

        let two: U = from_int(2);
        assert (two.to_int() == 2);

        assert (ten.add(&two) == from_int(12));
        assert (ten.sub(&two) == from_int(8));
        assert (ten.mul(&two) == from_int(20));
        assert (ten.div(&two) == from_int(5));
        assert (ten.modulo(&two) == from_int(0));
        assert (ten.neg() == from_int(-10));
    }

    test(10 as T);
}

#[test]
fn test_times() {
    use iter::Times;
    let ten = 10 as T;
    let mut accum = 0;
    for ten.times { accum += 1; }
    assert (accum == 10);
}

#[test]
#[should_fail]
#[ignore(cfg(windows))]
fn test_times_negative() {
    use iter::Times;
    for (-10).times { log(error, ~"nope!"); }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::path::Path;

fn main() {
    println!("cargo:rustc-link-lib=indy");
    println!("cargo:rustc-link-lib=sodium");

    let target = env::var("TARGET").unwrap();
    println!("target={}", target);

    match target.find("-windows-") {
        Some(..) => {
            println!("cargo:rustc-link-lib=ssleay32");
            println!("cargo:rustc-link-lib=zmq-pw");

            // TODO: FIXME: Provide more reliable dependencies resolving
            let output_dir = env::var("OUT_DIR").unwrap();
            let prebuilt_dir = env::var("INDY_DIR").unwrap();

            let dst = Path::new(&output_dir[..]).join("..\\..\\..");
            let prebuilt_lib = Path::new(&prebuilt_dir[..]).join("lib");

            println!("cargo:rustc-link-search=native={}", prebuilt_dir);
            println!("cargo:rustc-flags=-L {}/lib", prebuilt_dir);
            println!("cargo:include={}/include", prebuilt_dir);

            let files = vec!["indy.dll", "libeay32md.dll", "libsodium.dll", "libzmq-pw.dll", "ssleay32md.dll"];
            for f in files.iter() {
                if let Ok(_) = fs::copy(&prebuilt_lib.join(f), &dst.join(f)) {
                    println!("copy {} -> {}", &prebuilt_lib.join(f).display(), &dst.join(f).display());
                }
            }
            return;
        }
        None => {
            println!("cargo:rustc-link-lib=ssl");
        }
    }
}
<commit_msg>Modified cli/build.rs file<commit_after>use std::env;
use std::fs;
use std::path::Path;

fn main() {
    println!("cargo:rustc-link-lib=indy");

    let target = env::var("TARGET").unwrap();
    println!("target={}", target);

    if target.find("-windows-").is_some() {
        println!("cargo:rustc-link-lib=ssleay32");
        println!("cargo:rustc-link-lib=zmq-pw");
        println!("cargo:rustc-link-lib=sodium");

        // TODO: FIXME: Provide more reliable dependencies resolving
        let output_dir = env::var("OUT_DIR").unwrap();
        let prebuilt_dir = env::var("INDY_DIR").unwrap();

        let dst = Path::new(&output_dir[..]).join("..\\..\\..");
        let prebuilt_lib = Path::new(&prebuilt_dir[..]).join("lib");

        println!("cargo:rustc-link-search=native={}", prebuilt_dir);
        println!("cargo:rustc-flags=-L {}/lib", prebuilt_dir);
        println!("cargo:include={}/include", prebuilt_dir);

        let files = vec!["indy.dll", "libeay32md.dll", "libsodium.dll", "libzmq-pw.dll", "ssleay32md.dll"];
        for f in files.iter() {
            if let Ok(_) = fs::copy(&prebuilt_lib.join(f), &dst.join(f)) {
                println!("copy {} -> {}", &prebuilt_lib.join(f).display(), &dst.join(f).display());
            }
        }
        return;
    }
}
<|endoftext|>