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

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

use futures::{stream, Future, Stream};
use tempfile::Builder;
use uuid::Uuid;

use grpcio::{ChannelBuilder, Environment, Result, WriteFlags};
use kvproto::import_sstpb::*;
use kvproto::kvrpcpb::*;
use kvproto::tikvpb::*;

use pd_client::PdClient;
use test_raftstore::*;
use test_sst_importer::*;
use tikv_util::HandyRwLock;

const CLEANUP_SST_MILLIS: u64 = 10;

fn new_cluster() -> (Cluster<ServerCluster>, Context) {
    let count = 1;
    let mut cluster = new_server_cluster(0, count);
    let cleanup_interval = Duration::from_millis(CLEANUP_SST_MILLIS);
    cluster.cfg.raft_store.cleanup_import_sst_interval.0 = cleanup_interval;
    cluster.run();

    let region_id = 1;
    let leader = cluster.leader_of_region(region_id).unwrap();
    let epoch = cluster.get_region_epoch(region_id);
    let mut ctx = Context::default();
    ctx.set_region_id(region_id);
    ctx.set_peer(leader);
    ctx.set_region_epoch(epoch);

    (cluster, ctx)
}

fn new_cluster_and_tikv_import_client(
) -> (Cluster<ServerCluster>, Context, TikvClient, ImportSstClient) {
    let (cluster, ctx) = new_cluster();

    let ch = {
        let env = Arc::new(Environment::new(1));
        let node = ctx.get_peer().get_store_id();
        ChannelBuilder::new(env).connect(cluster.sim.rl().get_addr(node))
    };
    let tikv = TikvClient::new(ch.clone());
    let import = ImportSstClient::new(ch);

    (cluster, ctx, tikv, import)
}

#[test]
fn test_upload_sst() {
    let (_cluster, ctx, _, import) = new_cluster_and_tikv_import_client();

    let data = vec![1; 1024];
    let crc32 = calc_data_crc32(&data);
    let length = data.len() as u64;

    // Mismatch crc32
    let meta = new_sst_meta(0, length);
    assert!(send_upload_sst(&import, &meta, &data).is_err());

    // Mismatch length
    let meta = new_sst_meta(crc32, 0);
    assert!(send_upload_sst(&import, &meta, &data).is_err());

    let mut meta = new_sst_meta(crc32, length);
    meta.set_region_id(ctx.get_region_id());
    meta.set_region_epoch(ctx.get_region_epoch().clone());
    send_upload_sst(&import, &meta, &data).unwrap();

    // Can't upload the same uuid file again.
    assert!(send_upload_sst(&import, &meta, &data).is_err());
}

#[test]
fn test_ingest_sst() {
    let (_cluster, ctx, tikv, import) = new_cluster_and_tikv_import_client();

    let temp_dir = Builder::new().prefix("test_ingest_sst").tempdir().unwrap();

    let sst_path = temp_dir.path().join("test.sst");
    let sst_range = (0, 100);
    let (mut meta, data) = gen_sst_file(sst_path, sst_range);

    // No region id and epoch.
    send_upload_sst(&import, &meta, &data).unwrap();

    let mut ingest = IngestRequest::default();
    ingest.set_context(ctx.clone());
    ingest.set_sst(meta.clone());
    let resp = import.ingest(&ingest).unwrap();
    assert!(resp.has_error());

    // Set region id and epoch.
    meta.set_region_id(ctx.get_region_id());
    meta.set_region_epoch(ctx.get_region_epoch().clone());
    send_upload_sst(&import, &meta, &data).unwrap();
    // Cann't upload the same file again.
    assert!(send_upload_sst(&import, &meta, &data).is_err());

    ingest.set_sst(meta.clone());
    let resp = import.ingest(&ingest).unwrap();
    assert!(!resp.has_error());

    // Set crc32 == 0 and length != 0 still ingest success
    send_upload_sst(&import, &meta, &data).unwrap();
    let crc32 = meta.get_crc32();
    meta.set_crc32(0);
    meta.set_length(data.len() as u64);
    ingest.set_sst(meta.clone());
    let resp = import.ingest(&ingest).unwrap();
    assert!(!resp.has_error());
    meta.set_crc32(crc32);

    // Check ingested kvs
    check_ingested_kvs(&tikv, &ctx, sst_range);

    // Upload the same file again to check if the ingested file has been deleted.
    send_upload_sst(&import, &meta, &data).unwrap();
}

#[test]
fn test_download_sst() {
    let (_cluster, ctx, tikv, import) = new_cluster_and_tikv_import_client();
    let temp_dir = Builder::new()
        .prefix("test_download_sst")
        .tempdir()
        .unwrap();

    let sst_path = temp_dir.path().join("test.sst");
    let sst_range = (0, 100);
    let (mut meta, _) = gen_sst_file(sst_path, sst_range);
    meta.set_region_id(ctx.get_region_id());
    meta.set_region_epoch(ctx.get_region_epoch().clone());

    // Checks that downloading a non-existing storage returns error.
    let mut download = DownloadRequest::default();
    download.set_sst(meta.clone());
    download.set_storage_backend(external_storage::make_local_backend(temp_dir.path()));
    download.set_name("missing.sst".to_owned());

    let result = import.download(&download).unwrap();
    assert!(
        result.has_error(),
        "unexpected download reply: {:?}",
        result
    );

    // Checks that downloading an empty SST returns OK (but cannot be ingested)
    download.set_name("test.sst".to_owned());
    download.mut_sst().mut_range().set_start(vec![sst_range.1]);
    download
        .mut_sst()
        .mut_range()
        .set_end(vec![sst_range.1 + 1]);
    let result = import.download(&download).unwrap();
    assert!(result.get_is_empty());

    // Now perform a proper download.
    download.mut_sst().mut_range().set_start(Vec::new());
    download.mut_sst().mut_range().set_end(Vec::new());
    let result = import.download(&download).unwrap();
    assert!(!result.get_is_empty());
    assert_eq!(result.get_range().get_start(), &[sst_range.0]);
    assert_eq!(result.get_range().get_end(), &[sst_range.1 - 1]);

    // Do an ingest and verify the result is correct.

    let mut ingest = IngestRequest::default();
    ingest.set_context(ctx.clone());
    ingest.set_sst(meta);
    let resp = import.ingest(&ingest).unwrap();
    assert!(!resp.has_error());

    check_ingested_kvs(&tikv, &ctx, sst_range);
}

#[test]
fn test_cleanup_sst() {
    let (mut cluster, ctx, _, import) = new_cluster_and_tikv_import_client();

    let temp_dir = Builder::new().prefix("test_cleanup_sst").tempdir().unwrap();

    let sst_path = temp_dir.path().join("test_split.sst");
    let sst_range = (0, 100);
    let (mut meta, data) = gen_sst_file(sst_path, sst_range);
    meta.set_region_id(ctx.get_region_id());
    meta.set_region_epoch(ctx.get_region_epoch().clone());

    send_upload_sst(&import, &meta, &data).unwrap();

    // Can not upload the same file when it exists.
    assert!(send_upload_sst(&import, &meta, &data).is_err());

    // The uploaded SST should be deleted if the region split.
    let region = cluster.get_region(&[]);
    cluster.must_split(&region, &[100]);

    check_sst_deleted(&import, &meta, &data);

    let left = cluster.get_region(&[]);
    let right = cluster.get_region(&[100]);

    let sst_path = temp_dir.path().join("test_merge.sst");
    let sst_range = (0, 100);
    let (mut meta, data) = gen_sst_file(sst_path, sst_range);
    meta.set_region_id(left.get_id());
    meta.set_region_epoch(left.get_region_epoch().clone());

    send_upload_sst(&import, &meta, &data).unwrap();

    // The uploaded SST should be deleted if the region merged.
    cluster.pd_client.must_merge(left.get_id(), right.get_id());
    let res = cluster.pd_client.get_region_by_id(left.get_id());
    assert!(res.wait().unwrap().is_none());

    check_sst_deleted(&import, &meta, &data);
}

#[test]
fn test_ingest_sst_region_not_found() {
    let (_cluster, mut ctx_not_found, _, import) = new_cluster_and_tikv_import_client();

    let temp_dir = Builder::new()
        .prefix("test_ingest_sst_errors")
        .tempdir()
        .unwrap();

    ctx_not_found.set_region_id(1 << 31); // A large region id that must no exists.
    let sst_path = temp_dir.path().join("test_split.sst");
    let sst_range = (0, 100);
    let (mut meta, _data) = gen_sst_file(sst_path, sst_range);
    meta.set_region_id(ctx_not_found.get_region_id());
    meta.set_region_epoch(ctx_not_found.get_region_epoch().clone());

    let mut ingest = IngestRequest::default();
    ingest.set_context(ctx_not_found);
    ingest.set_sst(meta);
    let resp = import.ingest(&ingest).unwrap();
    assert!(resp.get_error().has_region_not_found());
}

fn new_sst_meta(crc32: u32, length: u64) -> SstMeta {
    let mut m = SstMeta::default();
    m.set_uuid(Uuid::new_v4().as_bytes().to_vec());
    m.set_crc32(crc32);
    m.set_length(length);
    m
}

fn send_upload_sst(
    client: &ImportSstClient,
    meta: &SstMeta,
    data: &[u8],
) -> Result<UploadResponse> {
    let mut r1 = UploadRequest::default();
    r1.set_meta(meta.clone());
    let mut r2 = UploadRequest::default();
    r2.set_data(data.to_vec());
    let reqs: Vec<_> = vec![r1, r2]
        .into_iter()
        .map(|r| (r, WriteFlags::default()))
        .collect();
    let (tx, rx) = client.upload().unwrap();
    let stream = stream::iter_ok(reqs);
    stream.forward(tx).and_then(|_| rx).wait()
}

fn check_ingested_kvs(tikv: &TikvClient, ctx: &Context, sst_range: (u8, u8)) {
    for i in sst_range.0..sst_range.1 {
        let mut m = RawGetRequest::default();
        m.set_context(ctx.clone());
        m.set_key(vec![i]);
        let resp = tikv.raw_get(&m).unwrap();
        assert!(resp.get_error().is_empty());
        assert!(!resp.has_region_error());
        assert_eq!(resp.get_value(), &[i]);
    }
}

fn check_sst_deleted(client: &ImportSstClient, meta: &SstMeta, data: &[u8]) {
    for _ in 0..10 {
        if send_upload_sst(client, meta, data).is_ok() {
            // If we can upload the file, it means the previous file has been deleted.
            return;
        }
        thread::sleep(Duration::from_millis(CLEANUP_SST_MILLIS));
    }
    send_upload_sst(client, meta, data).unwrap();
}
<commit_msg>tests: split test_ingest_sst into two tests (#7359)<commit_after>// Copyright 2018 TiKV Project Authors. Licensed under Apache-2.0.

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

use futures::{stream, Future, Stream};
use tempfile::Builder;
use uuid::Uuid;

use grpcio::{ChannelBuilder, Environment, Result, WriteFlags};
use kvproto::import_sstpb::*;
use kvproto::kvrpcpb::*;
use kvproto::tikvpb::*;

use pd_client::PdClient;
use test_raftstore::*;
use test_sst_importer::*;
use tikv_util::HandyRwLock;

const CLEANUP_SST_MILLIS: u64 = 10;

fn new_cluster() -> (Cluster<ServerCluster>, Context) {
    let count = 1;
    let mut cluster = new_server_cluster(0, count);
    let cleanup_interval = Duration::from_millis(CLEANUP_SST_MILLIS);
    cluster.cfg.raft_store.cleanup_import_sst_interval.0 = cleanup_interval;
    cluster.run();

    let region_id = 1;
    let leader = cluster.leader_of_region(region_id).unwrap();
    let epoch = cluster.get_region_epoch(region_id);
    let mut ctx = Context::default();
    ctx.set_region_id(region_id);
    ctx.set_peer(leader);
    ctx.set_region_epoch(epoch);

    (cluster, ctx)
}

fn new_cluster_and_tikv_import_client(
) -> (Cluster<ServerCluster>, Context, TikvClient, ImportSstClient) {
    let (cluster, ctx) = new_cluster();

    let ch = {
        let env = Arc::new(Environment::new(1));
        let node = ctx.get_peer().get_store_id();
        ChannelBuilder::new(env).connect(cluster.sim.rl().get_addr(node))
    };
    let tikv = TikvClient::new(ch.clone());
    let import = ImportSstClient::new(ch);

    (cluster, ctx, tikv, import)
}

#[test]
fn test_upload_sst() {
    let (_cluster, ctx, _, import) = new_cluster_and_tikv_import_client();

    let data = vec![1; 1024];
    let crc32 = calc_data_crc32(&data);
    let length = data.len() as u64;

    // Mismatch crc32
    let meta = new_sst_meta(0, length);
    assert!(send_upload_sst(&import, &meta, &data).is_err());

    // Mismatch length
    let meta = new_sst_meta(crc32, 0);
    assert!(send_upload_sst(&import, &meta, &data).is_err());

    let mut meta = new_sst_meta(crc32, length);
    meta.set_region_id(ctx.get_region_id());
    meta.set_region_epoch(ctx.get_region_epoch().clone());
    send_upload_sst(&import, &meta, &data).unwrap();

    // Can't upload the same uuid file again.
    assert!(send_upload_sst(&import, &meta, &data).is_err());
}

#[test]
fn test_ingest_sst() {
    let (_cluster, ctx, _tikv, import) = new_cluster_and_tikv_import_client();

    let temp_dir = Builder::new().prefix("test_ingest_sst").tempdir().unwrap();

    let sst_path = temp_dir.path().join("test.sst");
    let sst_range = (0, 100);
    let (mut meta, data) = gen_sst_file(sst_path, sst_range);

    // No region id and epoch.
    send_upload_sst(&import, &meta, &data).unwrap();

    let mut ingest = IngestRequest::default();
    ingest.set_context(ctx.clone());
    ingest.set_sst(meta.clone());
    let resp = import.ingest(&ingest).unwrap();
    assert!(resp.has_error());

    // Set region id and epoch.
    meta.set_region_id(ctx.get_region_id());
    meta.set_region_epoch(ctx.get_region_epoch().clone());
    send_upload_sst(&import, &meta, &data).unwrap();
    // Can't upload the same file again.
    assert!(send_upload_sst(&import, &meta, &data).is_err());

    ingest.set_sst(meta.clone());
    let resp = import.ingest(&ingest).unwrap();
    assert!(!resp.has_error());
}

#[test]
fn test_ingest_sst_without_crc32() {
    let (_cluster, ctx, tikv, import) = new_cluster_and_tikv_import_client();

    let temp_dir = Builder::new()
        .prefix("test_ingest_sst_without_crc32")
        .tempdir()
        .unwrap();

    let sst_path = temp_dir.path().join("test.sst");
    let sst_range = (0, 100);
    let (mut meta, data) = gen_sst_file(sst_path, sst_range);
    meta.set_region_id(ctx.get_region_id());
    meta.set_region_epoch(ctx.get_region_epoch().clone());

    // Set crc32 == 0 and length != 0 still ingest success
    send_upload_sst(&import, &meta, &data).unwrap();
    meta.set_crc32(0);

    let mut ingest = IngestRequest::default();
    ingest.set_context(ctx.clone());
    ingest.set_sst(meta.clone());
    let resp = import.ingest(&ingest).unwrap();
    assert!(!resp.has_error(), "{:?}", resp.get_error());

    // Check ingested kvs
    check_ingested_kvs(&tikv, &ctx, sst_range);
}

#[test]
fn test_download_sst() {
    let (_cluster, ctx, tikv, import) = new_cluster_and_tikv_import_client();
    let temp_dir = Builder::new()
        .prefix("test_download_sst")
        .tempdir()
        .unwrap();

    let sst_path = temp_dir.path().join("test.sst");
    let sst_range = (0, 100);
    let (mut meta, _) = gen_sst_file(sst_path, sst_range);
    meta.set_region_id(ctx.get_region_id());
    meta.set_region_epoch(ctx.get_region_epoch().clone());

    // Checks that downloading a non-existing storage returns error.
    let mut download = DownloadRequest::default();
    download.set_sst(meta.clone());
    download.set_storage_backend(external_storage::make_local_backend(temp_dir.path()));
    download.set_name("missing.sst".to_owned());

    let result = import.download(&download).unwrap();
    assert!(
        result.has_error(),
        "unexpected download reply: {:?}",
        result
    );

    // Checks that downloading an empty SST returns OK (but cannot be ingested)
    download.set_name("test.sst".to_owned());
    download.mut_sst().mut_range().set_start(vec![sst_range.1]);
    download
        .mut_sst()
        .mut_range()
        .set_end(vec![sst_range.1 + 1]);
    let result = import.download(&download).unwrap();
    assert!(result.get_is_empty());

    // Now perform a proper download.
    download.mut_sst().mut_range().set_start(Vec::new());
    download.mut_sst().mut_range().set_end(Vec::new());
    let result = import.download(&download).unwrap();
    assert!(!result.get_is_empty());
    assert_eq!(result.get_range().get_start(), &[sst_range.0]);
    assert_eq!(result.get_range().get_end(), &[sst_range.1 - 1]);

    // Do an ingest and verify the result is correct.

    let mut ingest = IngestRequest::default();
    ingest.set_context(ctx.clone());
    ingest.set_sst(meta);
    let resp = import.ingest(&ingest).unwrap();
    assert!(!resp.has_error());

    check_ingested_kvs(&tikv, &ctx, sst_range);
}

#[test]
fn test_cleanup_sst() {
    let (mut cluster, ctx, _, import) = new_cluster_and_tikv_import_client();

    let temp_dir = Builder::new().prefix("test_cleanup_sst").tempdir().unwrap();

    let sst_path = temp_dir.path().join("test_split.sst");
    let sst_range = (0, 100);
    let (mut meta, data) = gen_sst_file(sst_path, sst_range);
    meta.set_region_id(ctx.get_region_id());
    meta.set_region_epoch(ctx.get_region_epoch().clone());

    send_upload_sst(&import, &meta, &data).unwrap();

    // Can not upload the same file when it exists.
    assert!(send_upload_sst(&import, &meta, &data).is_err());

    // The uploaded SST should be deleted if the region split.
    let region = cluster.get_region(&[]);
    cluster.must_split(&region, &[100]);

    check_sst_deleted(&import, &meta, &data);

    let left = cluster.get_region(&[]);
    let right = cluster.get_region(&[100]);

    let sst_path = temp_dir.path().join("test_merge.sst");
    let sst_range = (0, 100);
    let (mut meta, data) = gen_sst_file(sst_path, sst_range);
    meta.set_region_id(left.get_id());
    meta.set_region_epoch(left.get_region_epoch().clone());

    send_upload_sst(&import, &meta, &data).unwrap();

    // The uploaded SST should be deleted if the region merged.
    cluster.pd_client.must_merge(left.get_id(), right.get_id());
    let res = cluster.pd_client.get_region_by_id(left.get_id());
    assert!(res.wait().unwrap().is_none());

    check_sst_deleted(&import, &meta, &data);
}

#[test]
fn test_ingest_sst_region_not_found() {
    let (_cluster, mut ctx_not_found, _, import) = new_cluster_and_tikv_import_client();

    let temp_dir = Builder::new()
        .prefix("test_ingest_sst_errors")
        .tempdir()
        .unwrap();

    ctx_not_found.set_region_id(1 << 31); // A large region id that must no exists.
    let sst_path = temp_dir.path().join("test_split.sst");
    let sst_range = (0, 100);
    let (mut meta, _data) = gen_sst_file(sst_path, sst_range);
    meta.set_region_id(ctx_not_found.get_region_id());
    meta.set_region_epoch(ctx_not_found.get_region_epoch().clone());

    let mut ingest = IngestRequest::default();
    ingest.set_context(ctx_not_found);
    ingest.set_sst(meta);
    let resp = import.ingest(&ingest).unwrap();
    assert!(resp.get_error().has_region_not_found());
}

fn new_sst_meta(crc32: u32, length: u64) -> SstMeta {
    let mut m = SstMeta::default();
    m.set_uuid(Uuid::new_v4().as_bytes().to_vec());
    m.set_crc32(crc32);
    m.set_length(length);
    m
}

fn send_upload_sst(
    client: &ImportSstClient,
    meta: &SstMeta,
    data: &[u8],
) -> Result<UploadResponse> {
    let mut r1 = UploadRequest::default();
    r1.set_meta(meta.clone());
    let mut r2 = UploadRequest::default();
    r2.set_data(data.to_vec());
    let reqs: Vec<_> = vec![r1, r2]
        .into_iter()
        .map(|r| (r, WriteFlags::default()))
        .collect();
    let (tx, rx) = client.upload().unwrap();
    let stream = stream::iter_ok(reqs);
    stream.forward(tx).and_then(|_| rx).wait()
}

fn check_ingested_kvs(tikv: &TikvClient, ctx: &Context, sst_range: (u8, u8)) {
    for i in sst_range.0..sst_range.1 {
        let mut m = RawGetRequest::default();
        m.set_context(ctx.clone());
        m.set_key(vec![i]);
        let resp = tikv.raw_get(&m).unwrap();
        assert!(resp.get_error().is_empty());
        assert!(!resp.has_region_error());
        assert_eq!(resp.get_value(), &[i]);
    }
}

fn check_sst_deleted(client: &ImportSstClient, meta: &SstMeta, data: &[u8]) {
    for _ in 0..10 {
        if send_upload_sst(client, meta, data).is_ok() {
            // If we can upload the file, it means the previous file has been deleted.
            return;
        }
        thread::sleep(Duration::from_millis(CLEANUP_SST_MILLIS));
    }
    send_upload_sst(client, meta, data).unwrap();
}
<|endoftext|>


<commit_before>use super::bls::{Keypair, PublicKey};
use super::{Address, Hash256};

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ValidatorStatus {
    PendingActivation = 0,
    Active = 1,
    PendingExit = 2,
    PendingWithdraw = 3,
    Withdrawn = 5,
    Penalized = 127,
}

#[derive(Debug, Clone, PartialEq)]
pub struct ValidatorRecord {
    pub pubkey: PublicKey,
    pub withdrawal_shard: u16,
    pub withdrawal_address: Address,
    pub randao_commitment: Hash256,
    pub randao_last_change: u64,
    pub balance: u64,
    pub status: u8,
    pub exit_slot: u64,
}

impl ValidatorRecord {
    /// Generates a new instance where the keypair is generated using
    /// `rand::thread_rng` entropy and all other fields are set to zero.
    ///
    /// Returns the new instance and new keypair.
    pub fn zero_with_thread_rand_keypair() -> (Self, Keypair) {
        let keypair = Keypair::random();
        let s = Self {
            pubkey: keypair.pk.clone(),
            withdrawal_shard: 0,
            withdrawal_address: Address::zero(),
            randao_commitment: Hash256::zero(),
            randao_last_change: 0,
            balance: 0,
            status: 0,
            exit_slot: 0,
        };
        (s, keypair)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_validator_record_zero_rand_keypair() {
        let (v, _kp) = ValidatorRecord::zero_with_thread_rand_keypair();
        assert_eq!(v.withdrawal_shard, 0);
        assert!(v.withdrawal_address.is_zero());
        assert!(v.randao_commitment.is_zero());
        assert_eq!(v.randao_last_change, 0);
        assert_eq!(v.balance, 0);
        assert_eq!(v.status, 0);
        assert_eq!(v.exit_slot, 0);
    }
}
<commit_msg>struct updated accoding to spec<commit_after>use super::bls::{Keypair, PublicKey};
use super::{Address, Hash256};

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ValidatorStatus {
    PendingActivation = 0,
    Active = 1,
    PendingExit = 2,
    PendingWithdraw = 3,
    Withdrawn = 5,
    Penalized = 127,
}

#[derive(Debug, Clone, PartialEq)]
pub struct ValidatorRecord {
    pub pubkey: PublicKey,
    pub withdrawal_credentials: Hash32,
    pub randao_commitment: Hash32,
    pub randao_skips: u64,
    pub balance: u64,
    pub status: u64,
    pub latest_status_change_slot: u64,
    pub exit_count: u64
}

impl ValidatorRecord {
    /// Generates a new instance where the keypair is generated using
    /// `rand::thread_rng` entropy and all other fields are set to zero.
    ///
    /// Returns the new instance and new keypair.
    pub fn zero_with_thread_rand_keypair() -> (Self, Keypair) {
        let keypair = Keypair::random();
        let s = Self {
            pubkey: keypair.pk.clone(),
            withdrawal_shard: 0,
            withdrawal_address: Address::zero(),
            randao_commitment: Hash256::zero(),
            randao_last_change: 0,
            balance: 0,
            status: 0,
            exit_slot: 0,
        };
        (s, keypair)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_validator_record_zero_rand_keypair() {
        let (v, _kp) = ValidatorRecord::zero_with_thread_rand_keypair();
        assert_eq!(v.withdrawal_shard, 0);
        assert!(v.withdrawal_address.is_zero());
        assert!(v.randao_commitment.is_zero());
        assert_eq!(v.randao_last_change, 0);
        assert_eq!(v.balance, 0);
        assert_eq!(v.status, 0);
        assert_eq!(v.exit_slot, 0);
    }
}
<|endoftext|>


<commit_before>extern crate termion;
extern crate rand;

use std::io::{self, Read, Write};
use std::collections::{HashSet, HashMap};

use termion::{clear, cursor, style, color};
use termion::raw::IntoRawMode;
use termion::input::TermRead;
use termion::event::Key;
use rand::random;

struct Game<R, W: Write> {
    score: u32,
    grid: [u32; 16usize],
    bgs: HashMap<u32, color::Bg<color::Rgb>>,
    stdin: R,
    stdout: W,
}

fn init<R: Read, W: Write>(stdin: R, mut stdout: W) {
    write!(stdout, "{}", clear::All).unwrap();

    let mut bgs: HashMap<u32, color::Bg<color::Rgb>> = HashMap::with_capacity(17);
    // Ref: http://www.rapidtables.com/web/color/RGB_Color.htm
    // should cover all..
    let colors = [
        color::Bg(color::Rgb(224, 224, 224)), color::Bg(color::Rgb(255, 229, 204)),
        color::Bg(color::Rgb(255, 153, 153)), color::Bg(color::Rgb(204, 255, 209)),
        color::Bg(color::Rgb(204, 255, 255)), color::Bg(color::Rgb(204, 229, 255)),
        color::Bg(color::Rgb(229, 255, 204)), color::Bg(color::Rgb(229, 204, 255)),
        color::Bg(color::Rgb(255, 153, 255)), color::Bg(color::Rgb(255, 204, 229)),
        color::Bg(color::Rgb(255, 255, 51)), color::Bg(color::Rgb(255, 178, 102)),
        color::Bg(color::Rgb(178, 255, 102)), color::Bg(color::Rgb(102, 255, 178)),
        color::Bg(color::Rgb(102, 178, 255)), color::Bg(color::Rgb(102, 102, 255)),
        color::Bg(color::Rgb(255, 0, 0)),
    ];
    bgs.insert(0u32, colors[0]);
    for i in 1..17 {
        bgs.insert(1u32<<i as u32, colors[i]);
    }

    let mut game = Game {
        score: 0,
        grid: [0u32; 16usize],
        bgs: bgs,
        stdin: stdin.keys(),
        stdout: stdout,
    };

    game.reset();
    game.start();
}

impl <R, W: Write> Drop for Game<R, W> {
    fn drop(&mut self) {
        write!(self.stdout, "{}{}{}", clear::All, style::Reset,
               cursor::Goto(1, 1)).unwrap();
    }
}

macro_rules! continue_if_game_over {
    ($game_over:ident) => (
        if $game_over {
            continue;
        }
    )
}

impl <R: Iterator<Item=Result<Key, std::io::Error>>, W: Write> Game<R, W> {
    fn pos(&self, x: i32, y: i32) -> usize {
        (x * 4 + y) as usize
    }

    fn get_by_pos(&self, x: i32, y: i32) -> u32 {
        self.grid[self.pos(x, y)]
    }

    fn set_by_pos(&mut self, x: i32, y: i32, val: u32) {
        self.grid[self.pos(x, y)] = val;
    }

    fn start(&mut self) {
        let mut game_over = false;
        let mut moved = false;
        loop {
            let b = self.stdin.next().unwrap().unwrap();
            match b {
                Key::Up | Key::Char('w') | Key::Char('k') => {
                    continue_if_game_over!(game_over);
                    moved = self.up();
                },
                Key::Right | Key::Char('d') | Key::Char('l') => {
                    continue_if_game_over!(game_over);
                    moved = self.right();
                },
                Key::Down | Key::Char('s') | Key::Char('j') => {
                    continue_if_game_over!(game_over);
                    moved = self.down();
                },
                Key::Left | Key::Char('a') | Key::Char('h') => {
                    continue_if_game_over!(game_over);
                    moved = self.left();
                },
                Key::Char('r') => {
                    self.restart();
                    return;
                },
                Key::Esc | Key::Char('q') => return,
                _ => continue,
            }

            if !moved || game_over {
                continue;
            }
            let full_filled = self.fill_up();
            self.draw();
            if full_filled && self.game_over() {
                game_over = true;
                self.pop_menu();
            }
        }
    }

    fn reset(&mut self) {
        for i in 0..16usize {
            self.grid[i] = 0u32;
        }
        self.score = 0u32;
        self.game_over = false;
        let idx1 = random::<u8>() as usize % 16usize;
        self.grid[idx1] = 2u32;
        loop {
            let idx2 = random::<u8>() as usize % 16usize;
            if idx1 != idx2 {
                self.grid[idx2] = 2u32;
                break;
            }
        }
        self.draw();
    }

    fn restart(&mut self) {
        self.reset();
        self.start();
    }

    fn fill_up(&mut self) -> bool {
        let mut holes = HashSet::new();
        for i in 0..16usize {
            if self.grid[i] == 0u32 {
                holes.insert(i);
            }
        }

        let len = holes.len();
        if len == 0 {
            return true;
        }
        for idx in holes.iter() {  // use HashSet's random feature directly..
            // self.grid[*idx] = 1u32 << (*idx as u32 % 2u32 + 1u32);  // whatever...
            let mut threhold = 222u8;
            if len <= 4 {
                threhold = 127u8;
            }
            if random::<u8>() <= threhold {
                self.grid[*idx] = 2u32;
            } else {
                self.grid[*idx] = 4u32;
            }
            break;
        }

        (len-1) == 0
    }

    fn game_over(&mut self) -> bool {
        for x in 0..4 {
            for y in 1..4 {
                if self.get_by_pos(x, y-1) == self.get_by_pos(x, y) {
                    return false;
                }
            }
        }
        for y in 0..4 {
            for x in 1..4 {
                if self.get_by_pos(x-1, y) == self.get_by_pos(x, y) {
                    return false;
                }
            }
        }
        return true;
    }

    fn bg(&self, n: u32) -> color::Bg<color::Rgb> {
        match self.bgs.get(&n) {
            Some(bg) => *bg,
            None => color::Bg(color::Rgb(255, 255, 255)),
        }
    }

    fn draw(&mut self) {
        write!(self.stdout, "{}", cursor::Goto(1, 1)).unwrap();

        // header
        let header_bg = color::Bg(color::Rgb(128, 128, 128));
        write!(self.stdout, " {}{:31}{}\n\r", header_bg, " ", style::Reset).unwrap();
        let score = self.score.to_string();
        write!(self.stdout, " {}{} SCORE: {}{:space$}{}\n\r", header_bg, style::Bold, score,
               " ", style::Reset, space=23-score.len()).unwrap();
        write!(self.stdout, " {}{:31}{}\n\r", header_bg, " ", style::Reset).unwrap();
        write!(self.stdout, " {:32}\n\r", " ").unwrap();

        // body
        for x in 0..4i32 {
            let mut up_line: String = String::new();
            let mut mid_line: String = String::new();
            let mut down_line: String = String::new();
            for y in 0..4i32 {
                let val = self.get_by_pos(x, y);
                let sval = val.to_string();
                let bg = self.bg(val);

                up_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
                if val == 0u32 {
                    mid_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
                } else {
                    let pad = (7 - sval.len()) / 2;
                    mid_line.push_str(
                        format!(" {}{:lpad$}{}{}{}{}{:rpad$}{}", bg, " ", color::Fg(color::Black),
                                sval, style::Reset, bg, " ", style::Reset,
                                lpad=pad, rpad=7-sval.len()-pad).as_str());
                }
                down_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
            }
            up_line.push_str("\n\r");
            mid_line.push_str("\n\r");
            down_line.push_str("\n\r");
            self.stdout.write(up_line.as_bytes()).unwrap();
            self.stdout.write(mid_line.as_bytes()).unwrap();
            self.stdout.write(down_line.as_bytes()).unwrap();
            self.stdout.write(format!("{:32}\n\r", " ").as_bytes()).unwrap();
        }
        self.stdout.flush().unwrap();
    }

    fn pop_menu(&mut self) {
        let bg = color::Bg(color::Rgb(128, 128, 128));
        write!(self.stdout, "{}{}{}  GAME OVER!   {}\n\r",
               cursor::Goto(10, 4), bg, style::Bold, style::Reset).unwrap();
        write!(self.stdout, "{}{}  restart: r   {}\n\r",
               cursor::Goto(10, 5), bg, style::Reset).unwrap();
        write!(self.stdout, "{}{}  quit: ESC|q  {}\n\r",
               cursor::Goto(10, 6), bg, style::Reset).unwrap();
        write!(self.stdout, "{}{}               {}\n\r",
               cursor::Goto(10, 7), bg, style::Reset).unwrap();
    }

    fn merge<Fget, Fset>(&mut self, xs: [i32; 4], ys: [i32; 4],
                         mut fget: Fget, mut fset: Fset) -> bool
        where Fget: FnMut(&Game<R, W>, i32, i32) -> u32, Fset: FnMut(&mut Game<R, W>, i32, i32, u32) {
        let mut moved = false;  // Fuck: https://github.com/rust-lang/rust/issues/28570

        for x in xs.iter() {
            let mut prev_non_zero_y = ys[0];
            let first_y = ys[0];
            for y in (&ys[1..4]).iter() {
                let mut val = fget(self, *x, *y);
                if val != 0u32 {
                    if prev_non_zero_y < *y && fget(self, *x, prev_non_zero_y) == val {
                        let score = val * 2;
                        self.score += score;
                        fset(self, *x, prev_non_zero_y, score);
                        val = 0u32;
                        fset(self, *x, *y, val);
                        prev_non_zero_y = *y + 1;
                        moved = true;
                    } else {
                        prev_non_zero_y = *y;
                    }
                }

                let mut k = *y;
                while k > first_y {
                    let prev = fget(self, *x, k-1);
                    if prev != 0u32 || val == 0u32 {
                        break;
                    }
                    moved = true;
                    fset(self, *x, k-1, val);
                    fset(self, *x, k, 0u32);
                    k -= 1;
                    prev_non_zero_y = k;
                }
            }
        }

        moved
    }

    fn up(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(y, x);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(y, x, val);
        let xs = [0, 1, 2, 3];
        let ys = [0, 1, 2, 3];
        return self.merge(xs, ys, fget, fset);
    }

    fn right(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(-x, -y);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(-x, -y, val);
        let xs = [-3, -2, -1, 0];
        let ys = [-3, -2, -1, 0];
        return self.merge(xs, ys, fget, fset);
    }

    fn down(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(-y, -x);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(-y, -x, val);
        let xs = [-3, -2, -1, 0];
        let ys = [-3, -2, -1, 0];
        return self.merge(xs, ys, fget, fset);
    }

    fn left(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(x, y);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(x, y, val);
        let xs = [0, 1, 2, 3];
        let ys = [0, 1, 2, 3];
        return self.merge(xs, ys, fget, fset);
    }
}

fn main() {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let stdout = stdout.into_raw_mode().unwrap();
    let stdin = io::stdin();
    let stdin = stdin.lock();
    let stderr = io::stderr();
    let mut stderr = stderr.lock();

    let termsize = termion::terminal_size().ok();
    let (termwidth, termheight) = termsize.map(|(w, h)| (w-2, h-2)).unwrap();
    if termwidth < 32 || termheight < 20 {
        write!(stderr, "{}{}terminal size must satisfy with (width >= 32, height >= 20){}",
            style::Bold, color::Fg(color::Red), style::Reset).unwrap();
    } else {
        init(stdin, stdout);
    }
}
<commit_msg>remove unused member<commit_after>extern crate termion;
extern crate rand;

use std::io::{self, Read, Write};
use std::collections::{HashSet, HashMap};

use termion::{clear, cursor, style, color};
use termion::raw::IntoRawMode;
use termion::input::TermRead;
use termion::event::Key;
use rand::random;

struct Game<R, W: Write> {
    score: u32,
    grid: [u32; 16usize],
    bgs: HashMap<u32, color::Bg<color::Rgb>>,
    stdin: R,
    stdout: W,
}

fn init<R: Read, W: Write>(stdin: R, mut stdout: W) {
    write!(stdout, "{}", clear::All).unwrap();

    let mut bgs: HashMap<u32, color::Bg<color::Rgb>> = HashMap::with_capacity(17);
    // Ref: http://www.rapidtables.com/web/color/RGB_Color.htm
    // should cover all..
    let colors = [
        color::Bg(color::Rgb(224, 224, 224)), color::Bg(color::Rgb(255, 229, 204)),
        color::Bg(color::Rgb(255, 153, 153)), color::Bg(color::Rgb(204, 255, 209)),
        color::Bg(color::Rgb(204, 255, 255)), color::Bg(color::Rgb(204, 229, 255)),
        color::Bg(color::Rgb(229, 255, 204)), color::Bg(color::Rgb(229, 204, 255)),
        color::Bg(color::Rgb(255, 153, 255)), color::Bg(color::Rgb(255, 204, 229)),
        color::Bg(color::Rgb(255, 255, 51)), color::Bg(color::Rgb(255, 178, 102)),
        color::Bg(color::Rgb(178, 255, 102)), color::Bg(color::Rgb(102, 255, 178)),
        color::Bg(color::Rgb(102, 178, 255)), color::Bg(color::Rgb(102, 102, 255)),
        color::Bg(color::Rgb(255, 0, 0)),
    ];
    bgs.insert(0u32, colors[0]);
    for i in 1..17 {
        bgs.insert(1u32<<i as u32, colors[i]);
    }

    let mut game = Game {
        score: 0,
        grid: [0u32; 16usize],
        bgs: bgs,
        stdin: stdin.keys(),
        stdout: stdout,
    };

    game.reset();
    game.start();
}

impl <R, W: Write> Drop for Game<R, W> {
    fn drop(&mut self) {
        write!(self.stdout, "{}{}{}", clear::All, style::Reset,
               cursor::Goto(1, 1)).unwrap();
    }
}

macro_rules! continue_if_game_over {
    ($game_over:ident) => (
        if $game_over {
            continue;
        }
    )
}

impl <R: Iterator<Item=Result<Key, std::io::Error>>, W: Write> Game<R, W> {
    fn pos(&self, x: i32, y: i32) -> usize {
        (x * 4 + y) as usize
    }

    fn get_by_pos(&self, x: i32, y: i32) -> u32 {
        self.grid[self.pos(x, y)]
    }

    fn set_by_pos(&mut self, x: i32, y: i32, val: u32) {
        self.grid[self.pos(x, y)] = val;
    }

    fn start(&mut self) {
        let mut game_over = false;
        let mut moved = false;
        loop {
            let b = self.stdin.next().unwrap().unwrap();
            match b {
                Key::Up | Key::Char('w') | Key::Char('k') => {
                    continue_if_game_over!(game_over);
                    moved = self.up();
                },
                Key::Right | Key::Char('d') | Key::Char('l') => {
                    continue_if_game_over!(game_over);
                    moved = self.right();
                },
                Key::Down | Key::Char('s') | Key::Char('j') => {
                    continue_if_game_over!(game_over);
                    moved = self.down();
                },
                Key::Left | Key::Char('a') | Key::Char('h') => {
                    continue_if_game_over!(game_over);
                    moved = self.left();
                },
                Key::Char('r') => {
                    self.restart();
                    return;
                },
                Key::Esc | Key::Char('q') => return,
                _ => continue,
            }

            if !moved || game_over {
                continue;
            }
            let full_filled = self.fill_up();
            self.draw();
            if full_filled && self.game_over() {
                game_over = true;
                self.pop_menu();
            }
        }
    }

    fn reset(&mut self) {
        for i in 0..16usize {
            self.grid[i] = 0u32;
        }
        self.score = 0u32;
        let idx1 = random::<u8>() as usize % 16usize;
        self.grid[idx1] = 2u32;
        loop {
            let idx2 = random::<u8>() as usize % 16usize;
            if idx1 != idx2 {
                self.grid[idx2] = 2u32;
                break;
            }
        }
        self.draw();
    }

    fn restart(&mut self) {
        self.reset();
        self.start();
    }

    fn fill_up(&mut self) -> bool {
        let mut holes = HashSet::new();
        for i in 0..16usize {
            if self.grid[i] == 0u32 {
                holes.insert(i);
            }
        }

        let len = holes.len();
        if len == 0 {
            return true;
        }
        for idx in holes.iter() {  // use HashSet's random feature directly..
            // self.grid[*idx] = 1u32 << (*idx as u32 % 2u32 + 1u32);  // whatever...
            let mut threhold = 222u8;
            if len <= 4 {
                threhold = 127u8;
            }
            if random::<u8>() <= threhold {
                self.grid[*idx] = 2u32;
            } else {
                self.grid[*idx] = 4u32;
            }
            break;
        }

        (len-1) == 0
    }

    fn game_over(&mut self) -> bool {
        for x in 0..4 {
            for y in 1..4 {
                if self.get_by_pos(x, y-1) == self.get_by_pos(x, y) {
                    return false;
                }
            }
        }
        for y in 0..4 {
            for x in 1..4 {
                if self.get_by_pos(x-1, y) == self.get_by_pos(x, y) {
                    return false;
                }
            }
        }
        return true;
    }

    fn bg(&self, n: u32) -> color::Bg<color::Rgb> {
        match self.bgs.get(&n) {
            Some(bg) => *bg,
            None => color::Bg(color::Rgb(255, 255, 255)),
        }
    }

    fn draw(&mut self) {
        write!(self.stdout, "{}", cursor::Goto(1, 1)).unwrap();

        // header
        let header_bg = color::Bg(color::Rgb(128, 128, 128));
        write!(self.stdout, " {}{:31}{}\n\r", header_bg, " ", style::Reset).unwrap();
        let score = self.score.to_string();
        write!(self.stdout, " {}{} SCORE: {}{:space$}{}\n\r", header_bg, style::Bold, score,
               " ", style::Reset, space=23-score.len()).unwrap();
        write!(self.stdout, " {}{:31}{}\n\r", header_bg, " ", style::Reset).unwrap();
        write!(self.stdout, " {:32}\n\r", " ").unwrap();

        // body
        for x in 0..4i32 {
            let mut up_line: String = String::new();
            let mut mid_line: String = String::new();
            let mut down_line: String = String::new();
            for y in 0..4i32 {
                let val = self.get_by_pos(x, y);
                let sval = val.to_string();
                let bg = self.bg(val);

                up_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
                if val == 0u32 {
                    mid_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
                } else {
                    let pad = (7 - sval.len()) / 2;
                    mid_line.push_str(
                        format!(" {}{:lpad$}{}{}{}{}{:rpad$}{}", bg, " ", color::Fg(color::Black),
                                sval, style::Reset, bg, " ", style::Reset,
                                lpad=pad, rpad=7-sval.len()-pad).as_str());
                }
                down_line.push_str(format!(" {}{:7}{}", bg, " ", style::Reset).as_str());
            }
            up_line.push_str("\n\r");
            mid_line.push_str("\n\r");
            down_line.push_str("\n\r");
            self.stdout.write(up_line.as_bytes()).unwrap();
            self.stdout.write(mid_line.as_bytes()).unwrap();
            self.stdout.write(down_line.as_bytes()).unwrap();
            self.stdout.write(format!("{:32}\n\r", " ").as_bytes()).unwrap();
        }
        self.stdout.flush().unwrap();
    }

    fn pop_menu(&mut self) {
        let bg = color::Bg(color::Rgb(128, 128, 128));
        write!(self.stdout, "{}{}{}  GAME OVER!   {}\n\r",
               cursor::Goto(10, 4), bg, style::Bold, style::Reset).unwrap();
        write!(self.stdout, "{}{}  restart: r   {}\n\r",
               cursor::Goto(10, 5), bg, style::Reset).unwrap();
        write!(self.stdout, "{}{}  quit: ESC|q  {}\n\r",
               cursor::Goto(10, 6), bg, style::Reset).unwrap();
        write!(self.stdout, "{}{}               {}\n\r",
               cursor::Goto(10, 7), bg, style::Reset).unwrap();
    }

    fn merge<Fget, Fset>(&mut self, xs: [i32; 4], ys: [i32; 4],
                         mut fget: Fget, mut fset: Fset) -> bool
        where Fget: FnMut(&Game<R, W>, i32, i32) -> u32, Fset: FnMut(&mut Game<R, W>, i32, i32, u32) {
        let mut moved = false;  // Fuck: https://github.com/rust-lang/rust/issues/28570

        for x in xs.iter() {
            let mut prev_non_zero_y = ys[0];
            let first_y = ys[0];
            for y in (&ys[1..4]).iter() {
                let mut val = fget(self, *x, *y);
                if val != 0u32 {
                    if prev_non_zero_y < *y && fget(self, *x, prev_non_zero_y) == val {
                        let score = val * 2;
                        self.score += score;
                        fset(self, *x, prev_non_zero_y, score);
                        val = 0u32;
                        fset(self, *x, *y, val);
                        prev_non_zero_y = *y + 1;
                        moved = true;
                    } else {
                        prev_non_zero_y = *y;
                    }
                }

                let mut k = *y;
                while k > first_y {
                    let prev = fget(self, *x, k-1);
                    if prev != 0u32 || val == 0u32 {
                        break;
                    }
                    moved = true;
                    fset(self, *x, k-1, val);
                    fset(self, *x, k, 0u32);
                    k -= 1;
                    prev_non_zero_y = k;
                }
            }
        }

        moved
    }

    fn up(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(y, x);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(y, x, val);
        let xs = [0, 1, 2, 3];
        let ys = [0, 1, 2, 3];
        return self.merge(xs, ys, fget, fset);
    }

    fn right(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(-x, -y);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(-x, -y, val);
        let xs = [-3, -2, -1, 0];
        let ys = [-3, -2, -1, 0];
        return self.merge(xs, ys, fget, fset);
    }

    fn down(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(-y, -x);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(-y, -x, val);
        let xs = [-3, -2, -1, 0];
        let ys = [-3, -2, -1, 0];
        return self.merge(xs, ys, fget, fset);
    }

    fn left(&mut self) -> bool {
        let fget = |g: &Game<R, W>, x: i32, y: i32| g.get_by_pos(x, y);
        let fset = |g: &mut Game<R, W>, x: i32, y: i32, val: u32| g.set_by_pos(x, y, val);
        let xs = [0, 1, 2, 3];
        let ys = [0, 1, 2, 3];
        return self.merge(xs, ys, fget, fset);
    }
}

fn main() {
    let stdout = io::stdout();
    let stdout = stdout.lock();
    let stdout = stdout.into_raw_mode().unwrap();
    let stdin = io::stdin();
    let stdin = stdin.lock();
    let stderr = io::stderr();
    let mut stderr = stderr.lock();

    let termsize = termion::terminal_size().ok();
    let (termwidth, termheight) = termsize.map(|(w, h)| (w-2, h-2)).unwrap();
    if termwidth < 32 || termheight < 20 {
        write!(stderr, "{}{}terminal size must satisfy with (width >= 32, height >= 20){}",
            style::Bold, color::Fg(color::Red), style::Reset).unwrap();
    } else {
        init(stdin, stdout);
    }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::f32::consts::{
  PI,
  FRAC_PI_2
};
use std::num::{
  sqrt,
  pow,
  sin,
  cos
};
use glfw;
use gl;
use std;
use gl::types::{
  GLfloat,
  GLint,
  GLuint
};
use cgmath::matrix::Matrix;
use cgmath::vector::{
  Vec3,
  Vec2,
  Vector
};
use glh = gl_helpers;
use camera::Camera;
use glfw_events::EventHandlers;
use map::TileIterator;
use color::Color3;

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 mvp_mat;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = mvp_mat * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

static PICK_VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  in vec3 color;
  out vec3 pass_color;
  uniform mat4 mvp_mat;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = mvp_mat * v;
    pass_color = color;
  }
";

static PICK_FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  in vec3 pass_color;
  out vec4 out_color;
  void main() {
    out_color = vec4(pass_color, 1.0);
  }
";

struct TilePicker {
  program: GLuint,
  color_buffer_obj: GLuint,
  mat_id: GLint,
  vertex_buffer_obj: GLuint,
  vertex_data: ~[Vec3<GLfloat>],
  color_data: ~[Color3],
  selected_tile_pos: Option<Vec2<int>>
}

impl TilePicker {
  fn new() -> TilePicker {
    TilePicker {
      color_buffer_obj: 0,
      program: 0,
      vertex_buffer_obj: 0,
      mat_id: 0,
      vertex_data: ~[],
      color_data: ~[],
      selected_tile_pos: None
    }
  }

  fn init_opengl(&mut self) {
    self.program = glh::compile_program(
      PICK_VERTEX_SHADER_SRC,
      PICK_FRAGMENT_SHADER_SRC);
  }

  pub fn cleanup_opengl(&self) {
    gl::DeleteProgram(self.program);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteBuffers(1, &self.color_buffer_obj);
    }
  }
}

impl Drop for TilePicker {
  fn drop(&mut self) {
    self.cleanup_opengl();
  }
}

pub struct Visualizer {
  hex_ex_radius: GLfloat,
  hex_in_radius: GLfloat,
  glfw_event_handlers: EventHandlers,
  program: GLuint,
  vertex_buffer_obj: GLuint,
  mat_id: GLint,
  win: Option<glfw::Window>,
  vertex_data: ~[Vec3<GLfloat>],
  mouse_pos: Vec2<f32>,
  camera: Camera,
  picker: TilePicker
}

fn init_win(win_size: Vec2<int>) -> glfw::Window {
  glfw::set_error_callback(~glfw::LogErrorHandler);
  glfw::init();
  let win = glfw::Window::create(
    win_size.x as u32,
    win_size.y as u32,
    "OpenGL",
    glfw::Windowed
  ).unwrap();
  win.make_context_current();
  win
}

impl Visualizer {
  pub fn new() -> ~Visualizer {
    let hex_ex_radius: GLfloat = 1.0 / 2.0;
    let hex_in_radius = sqrt(
        pow(hex_ex_radius, 2) - pow(hex_ex_radius / 2.0, 2));
    let win_size = Vec2::<int>{x: 640, y: 480};
    let win = init_win(win_size);
    let mut vis = ~Visualizer {
      hex_ex_radius: hex_ex_radius,
      hex_in_radius: hex_in_radius,
      glfw_event_handlers: EventHandlers::new(&win),
      program: 0,
      vertex_buffer_obj: 0,
      mat_id: 0,
      win: Some(win),
      vertex_data: ~[],
      mouse_pos: Vec2::zero(),
      camera: Camera::new(),
      picker: TilePicker::new()
    };
    vis.init_opengl();
    vis.init_model();
    vis.init_tile_picker();
    vis
  }

  pub fn v2i_to_v2f(&self, i: Vec2<i32>) -> Vec2<f32> {
    let v = Vec2 {
      x: (i.x as f32) * self.hex_in_radius * 2.0,
      y: (i.y as f32) * self.hex_ex_radius * 1.5
    };
    if i.y % 2 == 0 {
      Vec2{x: v.x + self.hex_in_radius, y: v.y}
    } else {
      v
    }
  }

  pub fn index_to_circle_vertex(&self, count: int, i: int) -> Vec2<f32> {
    let n = FRAC_PI_2 + 2.0 * PI * (i as f32) / (count as f32);
    Vec2{x: cos(n), y: sin(n)}.mul_s(self.hex_ex_radius)
  }

  pub fn index_to_hex_vertex(&self, i: int) -> Vec2<f32> {
    self.index_to_circle_vertex(6, i)
  }

  fn win<'a>(&'a self) -> &'a glfw::Window {
    self.win.get_ref()
  }

  fn build_hex_mesh(&mut self) {
    for tile_pos in TileIterator::new() {
      let pos3d = self.v2i_to_v2f(tile_pos).extend(0.0);
      for num in range(0, 6) {
        let vertex = self.index_to_hex_vertex(num);
        let next_vertex = self.index_to_hex_vertex(num + 1);
        let data = &mut self.vertex_data;
        data.push(pos3d + vertex.extend(0.0));
        data.push(pos3d + next_vertex.extend(0.0));
        data.push(pos3d + Vec3::zero());
      }
    }
  }

  fn init_model(&mut self) {
    self.build_hex_mesh();
    unsafe {
      gl::UseProgram(self.program);
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
      glh::fill_current_coord_vbo(self.vertex_data);
      let pos_attr = glh::get_attr(self.program, "position");
      gl::EnableVertexAttribArray(pos_attr);
      glh::define_array_of_generic_attr_data(pos_attr);
      self.mat_id = glh::get_uniform(self.program, "mvp_mat");
    }
  }

  fn init_opengl(&mut self) {
    gl::load_with(glfw::get_proc_address);
    self.program = glh::compile_program(
      VERTEX_SHADER_SRC,
      FRAGMENT_SHADER_SRC);
    self.picker.init_opengl();
  }

  pub fn cleanup_opengl(&self) {
    gl::DeleteProgram(self.program);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
    }
    self.picker.cleanup_opengl();
  }

  fn draw_map(&self) {
    gl::UseProgram(self.program);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
    glh::uniform_mat4f(self.mat_id, &self.camera.mat());
    glh::draw_mesh(self.vertex_data);
  }

  pub fn draw(&self) {
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    self.draw_map();
    self.win().swap_buffers();
  }

  pub fn is_running(&self) -> bool {
    return !self.win().should_close()
  }

  pub fn handle_key_events(&mut self) {
    self.glfw_event_handlers.key_handler.handle(|event| {
      if event.action != glfw::Press {
        return;
      }
      match event.key {
        glfw::KeyEscape | glfw::KeyQ
                       => self.win().set_should_close(true),
        glfw::KeySpace => println!("space"),
        glfw::KeyUp    => self.camera.move(270.0),
        glfw::KeyDown  => self.camera.move(90.0),
        glfw::KeyRight => self.camera.move(0.0),
        glfw::KeyLeft  => self.camera.move(180.0),
        _ => {}
      }
    });
  }

  pub fn handle_cursor_pos_events(&mut self) {
    self.glfw_event_handlers.cursor_pos_handler.handle(|event| {
      let button = self.win().get_mouse_button(glfw::MouseButtonRight);
      if button == glfw::Press {
        self.camera.z_angle += self.mouse_pos.x - event.x;
        self.camera.x_angle += self.mouse_pos.y - event.y;
      }
      self.mouse_pos = Vec2{x: event.x, y: event.y};
    });
  }

  pub fn handle_events(&mut self) {
    glfw::poll_events();
    self.handle_key_events();
    self.handle_cursor_pos_events();
  }

  fn close_window(&mut self) {
    // destroy glfw::Window before terminating glfw
    self.win = None;
  }

  fn build_hex_mesh_for_picking(&mut self) {
    for tile_pos in TileIterator::new() {
      let pos3d = self.v2i_to_v2f(tile_pos).extend(0.0);
      for num in range(0, 6) {
        let vertex = self.index_to_hex_vertex(num);
        let next_vertex = self.index_to_hex_vertex(num + 1);
        let col_x = tile_pos.x as f32 / 255.0;
        let col_y = tile_pos.y as f32 / 255.0;
        let c_data = &mut self.picker.color_data;
        let v_data = &mut self.picker.vertex_data;
        let color = Color3{r: col_x, g: col_y, b: 1.0};
        v_data.push(pos3d + vertex.extend(0.0));
        c_data.push(color);
        v_data.push(pos3d + next_vertex.extend(0.0));
        c_data.push(color);
        v_data.push(pos3d + Vec3::zero());
        c_data.push(color);
      }
    }
  }

  fn init_tile_picker(&mut self) {
    self.build_hex_mesh_for_picking();
    unsafe {
      gl::UseProgram(self.picker.program);
      gl::GenBuffers(1, &mut self.picker.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.picker.vertex_buffer_obj);
      glh::fill_current_coord_vbo(self.picker.vertex_data);
      let pos_attr = glh::get_attr(self.picker.program, "position");
      gl::EnableVertexAttribArray(pos_attr);
      glh::define_array_of_generic_attr_data(pos_attr);
      gl::GenBuffers(1, &mut self.picker.color_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.picker.color_buffer_obj);
      glh::fill_current_color_vbo(self.picker.color_data);
      let color_attr = glh::get_attr(self.picker.program, "color");
      gl::EnableVertexAttribArray(color_attr);
      glh::define_array_of_generic_attr_data(color_attr);
      self.picker.mat_id = glh::get_uniform(self.picker.program, "mvp_mat");
    }
  }

  fn _pick_tile(&self, x: i32, y: i32) -> Option<Vec2<int>> {
    let (_, height) = self.win().get_size();
    let reverted_y = height - y;
    let data: [u8, ..4] = [0, 0, 0, 0]; // mut
    unsafe {
      let data_ptr = std::cast::transmute(&data[0]);
      gl::ReadPixels(
        x, reverted_y, 1, 1,
        gl::RGBA,
        gl::UNSIGNED_BYTE,
        data_ptr
      );
    }
    if data[2] != 0 {
      Some(Vec2{x: data[0] as int, y: data[1] as int})
    } else {
      None
    }
  }

  pub fn pick_tile(&mut self) {
    gl::UseProgram(self.picker.program);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.picker.vertex_buffer_obj);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.picker.color_buffer_obj);
    glh::uniform_mat4f(self.picker.mat_id, &self.camera.mat());
    gl::ClearColor(0.0, 0.0, 0.0, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    glh::draw_mesh(self.picker.vertex_data);
    self.picker.selected_tile_pos = self._pick_tile(
      self.mouse_pos.x as i32,
      self.mouse_pos.y as i32
    );
    println!("selected: {:?}", self.picker.selected_tile_pos);
  }
}

impl Drop for Visualizer {
  fn drop(&mut self) {
    self.cleanup_opengl();
    self.close_window();
    glfw::terminate();
  }
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<commit_msg>visualizer.rs: Fixed TilePicker logic (mostly)<commit_after>// See LICENSE file for copyright and license details.

use std::f32::consts::{
  PI,
  FRAC_PI_2
};
use std::num::{
  sqrt,
  pow,
  sin,
  cos
};
use glfw;
use gl;
use std;
use gl::types::{
  GLfloat,
  GLint,
  GLuint
};
use cgmath::matrix::Matrix;
use cgmath::vector::{
  Vec3,
  Vec2,
  Vector
};
use glh = gl_helpers;
use camera::Camera;
use glfw_events::EventHandlers;
use map::TileIterator;
use color::Color3;

static VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  uniform mat4 mvp_mat;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = mvp_mat * v;
  }
";

static FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  out vec4 out_color;
  void main() {
    out_color = vec4(1.0, 1.0, 1.0, 1.0);
  }
";

static PICK_VERTEX_SHADER_SRC: &'static str = "
  #version 130
  in vec3 position;
  in vec3 color;
  out vec3 pass_color;
  uniform mat4 mvp_mat;
  void main() {
    vec4 v = vec4(position, 1);
    gl_Position = mvp_mat * v;
    pass_color = color;
  }
";

static PICK_FRAGMENT_SHADER_SRC: &'static str = "
  #version 130
  in vec3 pass_color;
  out vec4 out_color;
  void main() {
    out_color = vec4(pass_color, 1.0);
  }
";

struct TilePicker {
  program: GLuint,
  color_buffer_obj: GLuint,
  mat_id: GLint,
  vertex_buffer_obj: GLuint,
  vertex_data: ~[Vec3<GLfloat>],
  color_data: ~[Color3],
}

impl TilePicker {
  fn new() -> TilePicker {
    TilePicker {
      color_buffer_obj: 0,
      program: 0,
      vertex_buffer_obj: 0,
      mat_id: 0,
      vertex_data: ~[],
      color_data: ~[],
    }
  }

  fn init_opengl(&mut self) {
    self.program = glh::compile_program(
      PICK_VERTEX_SHADER_SRC,
      PICK_FRAGMENT_SHADER_SRC);
  }

  pub fn cleanup_opengl(&self) {
    gl::DeleteProgram(self.program);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
      gl::DeleteBuffers(1, &self.color_buffer_obj);
    }
  }

  fn build_hex_mesh_for_picking(&mut self, geom: &Geom) {
    for tile_pos in TileIterator::new() {
      let pos3d = geom.v2i_to_v2f(tile_pos).extend(0.0);
      for num in range(0, 6) {
        let vertex = geom.index_to_hex_vertex(num);
        let next_vertex = geom.index_to_hex_vertex(num + 1);
        let col_x = tile_pos.x as f32 / 255.0;
        let col_y = tile_pos.y as f32 / 255.0;
        let c_data = &mut self.color_data;
        let v_data = &mut self.vertex_data;
        let color = Color3{r: col_x, g: col_y, b: 1.0};
        v_data.push(pos3d + vertex.extend(0.0));
        c_data.push(color);
        v_data.push(pos3d + next_vertex.extend(0.0));
        c_data.push(color);
        v_data.push(pos3d + Vec3::zero());
        c_data.push(color);
      }
    }
  }

  // TODO: Call from 'new'?
  fn init(&mut self, geom: &Geom) {
    self.build_hex_mesh_for_picking(geom);
    unsafe {
      gl::UseProgram(self.program);
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
      glh::fill_current_coord_vbo(self.vertex_data);
      let pos_attr = glh::get_attr(self.program, "position");
      gl::EnableVertexAttribArray(pos_attr);
      glh::define_array_of_generic_attr_data(pos_attr);
      gl::GenBuffers(1, &mut self.color_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.color_buffer_obj);
      glh::fill_current_color_vbo(self.color_data);
      let color_attr = glh::get_attr(self.program, "color");
      gl::EnableVertexAttribArray(color_attr);
      glh::define_array_of_generic_attr_data(color_attr);
      self.mat_id = glh::get_uniform(self.program, "mvp_mat");
    }
  }

  fn _pick_tile(
    &self,
    win_size: (i32, i32),
    mouse_pos: Vec2<i32>
  ) -> Option<Vec2<i32>> {
    let (_, height) = win_size;
    let reverted_y = height - mouse_pos.y;
    let data: [u8, ..4] = [0, 0, 0, 0]; // mut
    unsafe {
      let data_ptr = std::cast::transmute(&data[0]);
      gl::ReadPixels(
        mouse_pos.x, reverted_y, 1, 1,
        gl::RGBA,
        gl::UNSIGNED_BYTE,
        data_ptr
      );
    }
    if data[2] != 0 {
      Some(Vec2{x: data[0] as i32, y: data[1] as i32})
    } else {
      None
    }
  }

  pub fn pick_tile(
    &mut self,
    win_size: (i32, i32),
    camera: &Camera,
    mouse_pos: Vec2<i32>
  ) -> Option<Vec2<i32>> {
    gl::UseProgram(self.program);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.color_buffer_obj);
    glh::uniform_mat4f(self.mat_id, &camera.mat());
    gl::ClearColor(0.0, 0.0, 0.0, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    glh::draw_mesh(self.vertex_data);
    self._pick_tile(win_size, mouse_pos)
  }
}

impl Drop for TilePicker {
  fn drop(&mut self) {
    self.cleanup_opengl();
  }
}

struct Geom {
  hex_ex_radius: GLfloat,
  hex_in_radius: GLfloat,
}

impl Geom {
  pub fn new() -> Geom {
    let hex_ex_radius: GLfloat = 1.0 / 2.0;
    let hex_in_radius = sqrt(
        pow(hex_ex_radius, 2) - pow(hex_ex_radius / 2.0, 2));
    Geom {
      hex_ex_radius: hex_ex_radius,
      hex_in_radius: hex_in_radius,
    }
  }

  pub fn v2i_to_v2f(&self, i: Vec2<i32>) -> Vec2<f32> {
    let v = Vec2 {
      x: (i.x as f32) * self.hex_in_radius * 2.0,
      y: (i.y as f32) * self.hex_ex_radius * 1.5
    };
    if i.y % 2 == 0 {
      Vec2{x: v.x + self.hex_in_radius, y: v.y}
    } else {
      v
    }
  }

  pub fn index_to_circle_vertex(&self, count: int, i: int) -> Vec2<f32> {
    let n = FRAC_PI_2 + 2.0 * PI * (i as f32) / (count as f32);
    Vec2{x: cos(n), y: sin(n)}.mul_s(self.hex_ex_radius)
  }

  pub fn index_to_hex_vertex(&self, i: int) -> Vec2<f32> {
    self.index_to_circle_vertex(6, i)
  }
}

pub struct Visualizer {
  glfw_event_handlers: EventHandlers,
  program: GLuint,
  vertex_buffer_obj: GLuint,
  mat_id: GLint,
  win: Option<glfw::Window>,
  vertex_data: ~[Vec3<GLfloat>],
  mouse_pos: Vec2<f32>,
  camera: Camera,
  picker: TilePicker,
  selected_tile_pos: Option<Vec2<i32>>,
  geom: Geom
}

fn init_win(win_size: Vec2<int>) -> glfw::Window {
  glfw::set_error_callback(~glfw::LogErrorHandler);
  glfw::init();
  let win = glfw::Window::create(
    win_size.x as u32,
    win_size.y as u32,
    "OpenGL",
    glfw::Windowed
  ).unwrap();
  win.make_context_current();
  win
}

impl Visualizer {
  pub fn new() -> ~Visualizer {
    let win_size = Vec2::<int>{x: 640, y: 480};
    let win = init_win(win_size);
    let mut vis = ~Visualizer {
      glfw_event_handlers: EventHandlers::new(&win),
      program: 0,
      vertex_buffer_obj: 0,
      mat_id: 0,
      win: Some(win),
      vertex_data: ~[],
      mouse_pos: Vec2::zero(),
      camera: Camera::new(),
      picker: TilePicker::new(),
      selected_tile_pos: None,
      geom: Geom::new()
    };
    vis.init_opengl();
    vis.init_model();
    vis.picker.init(&vis.geom);
    vis
  }

  fn win<'a>(&'a self) -> &'a glfw::Window {
    self.win.get_ref()
  }

  fn build_hex_mesh(&mut self) {
    for tile_pos in TileIterator::new() {
      let pos3d = self.geom.v2i_to_v2f(tile_pos).extend(0.0);
      for num in range(0, 6) {
        let vertex = self.geom.index_to_hex_vertex(num);
        let next_vertex = self.geom.index_to_hex_vertex(num + 1);
        let data = &mut self.vertex_data;
        data.push(pos3d + vertex.extend(0.0));
        data.push(pos3d + next_vertex.extend(0.0));
        data.push(pos3d + Vec3::zero());
      }
    }
  }

  fn init_model(&mut self) {
    self.build_hex_mesh();
    unsafe {
      gl::UseProgram(self.program);
      gl::GenBuffers(1, &mut self.vertex_buffer_obj);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
      glh::fill_current_coord_vbo(self.vertex_data);
      let pos_attr = glh::get_attr(self.program, "position");
      gl::EnableVertexAttribArray(pos_attr);
      glh::define_array_of_generic_attr_data(pos_attr);
      self.mat_id = glh::get_uniform(self.program, "mvp_mat");
    }
  }

  fn init_opengl(&mut self) {
    gl::load_with(glfw::get_proc_address);
    self.program = glh::compile_program(
      VERTEX_SHADER_SRC,
      FRAGMENT_SHADER_SRC);
    self.picker.init_opengl();
  }

  pub fn cleanup_opengl(&self) {
    gl::DeleteProgram(self.program);
    unsafe {
      gl::DeleteBuffers(1, &self.vertex_buffer_obj);
    }
    self.picker.cleanup_opengl();
  }

  fn draw_map(&self) {
    gl::UseProgram(self.program);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.vertex_buffer_obj);
    glh::uniform_mat4f(self.mat_id, &self.camera.mat());
    glh::draw_mesh(self.vertex_data);
  }

  pub fn draw(&self) {
    gl::ClearColor(0.3, 0.3, 0.3, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
    self.draw_map();
    self.win().swap_buffers();
  }

  pub fn is_running(&self) -> bool {
    return !self.win().should_close()
  }

  pub fn handle_key_events(&mut self) {
    self.glfw_event_handlers.key_handler.handle(|event| {
      if event.action != glfw::Press {
        return;
      }
      match event.key {
        glfw::KeyEscape | glfw::KeyQ
                       => self.win().set_should_close(true),
        glfw::KeySpace => println!("space"),
        glfw::KeyUp    => self.camera.move(270.0),
        glfw::KeyDown  => self.camera.move(90.0),
        glfw::KeyRight => self.camera.move(0.0),
        glfw::KeyLeft  => self.camera.move(180.0),
        _ => {}
      }
    });
  }

  pub fn handle_cursor_pos_events(&mut self) {
    self.glfw_event_handlers.cursor_pos_handler.handle(|event| {
      let button = self.win().get_mouse_button(glfw::MouseButtonRight);
      if button == glfw::Press {
        self.camera.z_angle += self.mouse_pos.x - event.x;
        self.camera.x_angle += self.mouse_pos.y - event.y;
      }
      self.mouse_pos = Vec2{x: event.x, y: event.y};
    });
  }

  pub fn handle_events(&mut self) {
    glfw::poll_events();
    self.handle_key_events();
    self.handle_cursor_pos_events();
  }

  fn close_window(&mut self) {
    // destroy glfw::Window before terminating glfw
    self.win = None;
  }

  pub fn pick_tile(&mut self) {
    let mouse_pos = Vec2 {
      x: self.mouse_pos.x as i32,
      y: self.mouse_pos.y as i32
    };
    let win_size = self.win().get_size();
    self.selected_tile_pos = self.picker.pick_tile(
      win_size, &self.camera, mouse_pos);
    println!("selected: {:?}", self.selected_tile_pos);
  }
}

impl Drop for Visualizer {
  fn drop(&mut self) {
    self.cleanup_opengl();
    self.close_window();
    glfw::terminate();
  }
}

// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab:
<|endoftext|>


<commit_before>//! Crate which contain the virtual machine which executes gluon programs
#![doc(html_root_url="https://docs.rs/gluon_vm/0.4.2")] // # GLUON

#[macro_use]
extern crate log;
#[cfg(test)]
extern crate env_logger;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate mopa;
#[macro_use]
extern crate collect_mac;
#[macro_use]
extern crate bitflags;
extern crate itertools;
extern crate pretty;
#[macro_use]
extern crate futures;

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[cfg(all(feature = "serde", test))]
#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate gluon_base as base;
extern crate gluon_check as check;

#[macro_use]
pub mod api;
pub mod channel;
pub mod compiler;
pub mod debug;
pub mod dynamic;
#[macro_use]
pub mod future;
pub mod gc;
pub mod macros;
pub mod thread;
pub mod primitives;
pub mod reference;
pub mod stack;
pub mod types;

mod array;
mod core;
mod field_map;
mod interner;
mod lazy;
mod source_map;
mod value;
mod vm;

use std::marker::PhantomData;

use api::ValueRef;
use value::Value;
use types::VmIndex;
use base::types::ArcType;
use base::symbol::Symbol;

unsafe fn forget_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
    ::std::mem::transmute(x)
}

#[derive(Copy, Clone, Debug)]
pub struct Variants<'a>(Value, PhantomData<&'a Value>);

impl<'a> Variants<'a> {
    /// Creates a new `Variants` value which assumes that `value` is rooted for the lifetime of the
    /// value
    pub unsafe fn new(value: &Value) -> Variants {
        Variants::with_root(*value, value)
    }

    pub unsafe fn with_root<T: ?Sized>(value: Value, _root: &T) -> Variants {
        Variants(value, PhantomData)
    }

    /// Returns an instance of `ValueRef` which allows users to safely retrieve the interals of a
    /// value
    pub fn as_ref(&self) -> ValueRef<'a> {
        unsafe { ValueRef::rooted_new(self.0) }
    }
}

/// Type returned from vm functions which may fail
pub type Result<T> = ::std::result::Result<T, Error>;

quick_error! {
    /// Representation of all possible errors that can occur when interacting with the `vm` crate
    #[derive(Debug, PartialEq)]
    pub enum Error {
        Dead {
        }
        UndefinedBinding(symbol: String) {
            display("Binding `{}` is not defined", symbol)
        }
        UndefinedField(typ: ArcType, field: String) {
            display("Type `{}` does not have the field `{}`", typ, field)
        }
        TypeAlreadyExists(symbol: String) {
            display("Type `{}` already exists", symbol)
        }
        GlobalAlreadyExists(symbol: Symbol) {
            display("Global `{}` already exists", symbol)
        }
        MetadataDoesNotExist(symbol: String) {
            display("No metadata exists for `{}`", symbol)
        }
        WrongType(expected: ArcType, actual: ArcType) {
            display("Expected a value of type `{}` but the inferred type was `{}`",
                    expected, actual)
        }
        OutOfMemory { limit: usize, needed: usize } {
            display("Thread is out of memory: Limit {}, needed {}", limit, needed)
        }
        StackOverflow(limit: VmIndex) {
            display("The stack has overflowed: Limit `{}`", limit)
        }
        Message(err: String) {
            display("{}", err)
        }
        Panic(err: String) {
            display("{}", err)
        }
    }
}

/// Internal types and functions exposed to the main `gluon` crate
pub mod internal {
    pub use value::{Value, ClosureDataDef, ValuePrinter};
}
<commit_msg>fix: Increase recursion limit to 1024<commit_after>//! Crate which contain the virtual machine which executes gluon programs
#![doc(html_root_url="https://docs.rs/gluon_vm/0.4.2")] // # GLUON
#![recursion_limit = "1024"]

#[macro_use]
extern crate log;
#[cfg(test)]
extern crate env_logger;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate mopa;
#[macro_use]
extern crate collect_mac;
#[macro_use]
extern crate bitflags;
extern crate itertools;
extern crate pretty;
#[macro_use]
extern crate futures;

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[cfg(all(feature = "serde", test))]
#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate gluon_base as base;
extern crate gluon_check as check;

#[macro_use]
pub mod api;
pub mod channel;
pub mod compiler;
pub mod debug;
pub mod dynamic;
#[macro_use]
pub mod future;
pub mod gc;
pub mod macros;
pub mod thread;
pub mod primitives;
pub mod reference;
pub mod stack;
pub mod types;

mod array;
mod core;
mod field_map;
mod interner;
mod lazy;
mod source_map;
mod value;
mod vm;

use std::marker::PhantomData;

use api::ValueRef;
use value::Value;
use types::VmIndex;
use base::types::ArcType;
use base::symbol::Symbol;

unsafe fn forget_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
    ::std::mem::transmute(x)
}

#[derive(Copy, Clone, Debug)]
pub struct Variants<'a>(Value, PhantomData<&'a Value>);

impl<'a> Variants<'a> {
    /// Creates a new `Variants` value which assumes that `value` is rooted for the lifetime of the
    /// value
    pub unsafe fn new(value: &Value) -> Variants {
        Variants::with_root(*value, value)
    }

    pub unsafe fn with_root<T: ?Sized>(value: Value, _root: &T) -> Variants {
        Variants(value, PhantomData)
    }

    /// Returns an instance of `ValueRef` which allows users to safely retrieve the interals of a
    /// value
    pub fn as_ref(&self) -> ValueRef<'a> {
        unsafe { ValueRef::rooted_new(self.0) }
    }
}

/// Type returned from vm functions which may fail
pub type Result<T> = ::std::result::Result<T, Error>;

quick_error! {
    /// Representation of all possible errors that can occur when interacting with the `vm` crate
    #[derive(Debug, PartialEq)]
    pub enum Error {
        Dead {
        }
        UndefinedBinding(symbol: String) {
            display("Binding `{}` is not defined", symbol)
        }
        UndefinedField(typ: ArcType, field: String) {
            display("Type `{}` does not have the field `{}`", typ, field)
        }
        TypeAlreadyExists(symbol: String) {
            display("Type `{}` already exists", symbol)
        }
        GlobalAlreadyExists(symbol: Symbol) {
            display("Global `{}` already exists", symbol)
        }
        MetadataDoesNotExist(symbol: String) {
            display("No metadata exists for `{}`", symbol)
        }
        WrongType(expected: ArcType, actual: ArcType) {
            display("Expected a value of type `{}` but the inferred type was `{}`",
                    expected, actual)
        }
        OutOfMemory { limit: usize, needed: usize } {
            display("Thread is out of memory: Limit {}, needed {}", limit, needed)
        }
        StackOverflow(limit: VmIndex) {
            display("The stack has overflowed: Limit `{}`", limit)
        }
        Message(err: String) {
            display("{}", err)
        }
        Panic(err: String) {
            display("{}", err)
        }
    }
}

/// Internal types and functions exposed to the main `gluon` crate
pub mod internal {
    pub use value::{Value, ClosureDataDef, ValuePrinter};
}
<|endoftext|>


<commit_before><commit_msg>Edit docs again!<commit_after><|endoftext|>


<commit_before>use indoc::indoc;

// Returns `path` with the cargo test data directory prepended.
fn data_path(path: Option<&str>) -> std::path::PathBuf {
    let mut result = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    result.push("tests");
    result.push("checksum-test-data");
    if let Some(provided_path) = path {
        result.push(provided_path);
    }
    result
}

#[test]
fn basic_checksum() {
    assert_eq!(
        libzakopane::checksum(data_path(Some("basic-test"))),
        indoc!(
            r#"
            8ec39490ae7374067429174fd55867628145b9d20b4871a10aba36d24f3a5a33  ./random-data-00
            a9f70c6a2c2e3a3f7269e7d897f846454204a312ca62115fed676647b485bd54  ./random-data-01
            "#
        )
    );
}

#[test]
fn checksum_hidden_target_directory() {
    assert_eq!(
        libzakopane::checksum(data_path(Some("hidden-target-test/.hidden/"))),
        indoc!(
            r#"
            4bd4b6cff60b4bd1f618ed8fa6bf20c86bc7bc297498b9d43612713cf756bbd8  ./random-data-00
            35190dff137f1cc1a08df389ab6d0ba091f20a12098e062780ce0b5ccd796129  ./random-data-01
            "#
        )
    );
}
<commit_msg>checksum: Adjust test `data_path()`<commit_after>use indoc::indoc;

// Returns `path` with the cargo test data directory prepended.
fn data_path(path: &str) -> std::path::PathBuf {
    let mut result = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    result.push("tests");
    result.push("checksum-test-data");
    result.push(path);
    result
}

#[test]
fn basic_checksum() {
    assert_eq!(
        libzakopane::checksum(data_path("basic-test")),
        indoc!(
            r#"
            8ec39490ae7374067429174fd55867628145b9d20b4871a10aba36d24f3a5a33  ./random-data-00
            a9f70c6a2c2e3a3f7269e7d897f846454204a312ca62115fed676647b485bd54  ./random-data-01
            "#
        )
    );
}

#[test]
fn checksum_hidden_target_directory() {
    assert_eq!(
        libzakopane::checksum(data_path("hidden-target-test/.hidden/")),
        indoc!(
            r#"
            4bd4b6cff60b4bd1f618ed8fa6bf20c86bc7bc297498b9d43612713cf756bbd8  ./random-data-00
            35190dff137f1cc1a08df389ab6d0ba091f20a12098e062780ce0b5ccd796129  ./random-data-01
            "#
        )
    );
}
<|endoftext|>


<commit_before>// Basic types
pub type Color = usize; // 0 or 1
pub type File = usize;  // from 0 to 7
pub type Rank = usize;  // from 0 to 7
pub type Square = usize;  // from 0 to 63
pub type PieceType = usize;  // from 0 to 5
pub type MoveType = usize;  // from 0 to 3
pub type CastlingRights = [(bool, bool); 2];  // (King-side, Queen-side)

// Color
pub const WHITE: Color = 0;
pub const BLACK: Color = 1;

// Piece types
pub const QUEEN: PieceType = 0;
pub const ROOK: PieceType = 1;
pub const BISHOP: PieceType = 2;
pub const KNIGHT: PieceType = 3;
pub const KING: PieceType = 4;
pub const PAWN: PieceType = 5;

// Move types
pub const MOVE_NORMAL: MoveType = 0;
pub const MOVE_ENPASSANT: MoveType = 1;
pub const MOVE_PROMOTION: MoveType = 2;
pub const MOVE_CASTLING: MoveType = 3;

// Ranks
pub const RANK_1: Rank = 0;
pub const RANK_2: Rank = 1;
pub const RANK_3: Rank = 2;
pub const RANK_4: Rank = 3;
pub const RANK_5: Rank = 4;
pub const RANK_6: Rank = 5;
pub const RANK_7: Rank = 6;
pub const RANK_8: Rank = 7;

// Files
pub const FILE_A: Rank = 0;
pub const FILE_B: Rank = 1;
pub const FILE_C: Rank = 2;
pub const FILE_D: Rank = 3;
pub const FILE_E: Rank = 4;
pub const FILE_F: Rank = 5;
pub const FILE_G: Rank = 6;
pub const FILE_H: Rank = 7;

// Squares
pub const A1: Square = 0 + 0*8;
pub const B1: Square = 1 + 0*8;
pub const C1: Square = 2 + 0*8;
pub const D1: Square = 3 + 0*8;
pub const E1: Square = 4 + 0*8;
pub const F1: Square = 5 + 0*8;
pub const G1: Square = 6 + 0*8;
pub const H1: Square = 7 + 0*8;

pub const A2: Square = 0 + 1*8;
pub const B2: Square = 1 + 1*8;
pub const C2: Square = 2 + 1*8;
pub const D2: Square = 3 + 1*8;
pub const E2: Square = 4 + 1*8;
pub const F2: Square = 5 + 1*8;
pub const G2: Square = 6 + 1*8;
pub const H2: Square = 7 + 1*8;

pub const A3: Square = 0 + 2*8;
pub const B3: Square = 1 + 2*8;
pub const C3: Square = 2 + 2*8;
pub const D3: Square = 3 + 2*8;
pub const E3: Square = 4 + 2*8;
pub const F3: Square = 5 + 2*8;
pub const G3: Square = 6 + 2*8;
pub const H3: Square = 7 + 2*8;

pub const A4: Square = 0 + 3*8;
pub const B4: Square = 1 + 3*8;
pub const C4: Square = 2 + 3*8;
pub const D4: Square = 3 + 3*8;
pub const E4: Square = 4 + 3*8;
pub const F4: Square = 5 + 3*8;
pub const G4: Square = 6 + 3*8;
pub const H4: Square = 7 + 3*8;

pub const A5: Square = 0 + 4*8;
pub const B5: Square = 1 + 4*8;
pub const C5: Square = 2 + 4*8;
pub const D5: Square = 3 + 4*8;
pub const E5: Square = 4 + 4*8;
pub const F5: Square = 5 + 4*8;
pub const G5: Square = 6 + 4*8;
pub const H5: Square = 7 + 4*8;

pub const A6: Square = 0 + 5*8;
pub const B6: Square = 1 + 5*8;
pub const C6: Square = 2 + 5*8;
pub const D6: Square = 3 + 5*8;
pub const E6: Square = 4 + 5*8;
pub const F6: Square = 5 + 5*8;
pub const G6: Square = 6 + 5*8;
pub const H6: Square = 7 + 5*8;

pub const A7: Square = 0 + 6*8;
pub const B7: Square = 1 + 6*8;
pub const C7: Square = 2 + 6*8;
pub const D7: Square = 3 + 6*8;
pub const E7: Square = 4 + 6*8;
pub const F7: Square = 5 + 6*8;
pub const G7: Square = 6 + 6*8;
pub const H7: Square = 7 + 6*8;

pub const A8: Square = 0 + 7*8;
pub const B8: Square = 1 + 7*8;
pub const C8: Square = 2 + 7*8;
pub const D8: Square = 3 + 7*8;
pub const E8: Square = 4 + 7*8;
pub const F8: Square = 5 + 7*8;
pub const G8: Square = 6 + 7*8;
pub const H8: Square = 7 + 7*8;

pub struct Move(u16);

impl Move {
    #[inline]
    pub fn new(m_type: MoveType, orig: Square, dest: Square, pp_type: PieceType) -> Move {
        assert!(m_type <= 3);
        assert!(orig <= 63);
        assert!(dest <= 63);
        assert!(pp_type <= 3);
        Move( ((m_type << 14) | (pp_type << 12) | (orig << 6) | dest) as u16 )
    }

    #[inline]
    pub fn move_type(&self) -> MoveType {
        ((self.0 & (0b11 << 14)) >> 14) as MoveType
    }

    #[inline]
    pub fn orig_square(&self) -> Square {
        (self.0 & (0b111111 << 6) >> 6) as Square
    }

    #[inline]
    pub fn dest_square(&self) -> Square {
        (self.0 & 0b111111) as Square
    }

    #[inline]
    pub fn promotion_piece_type(&self) -> PieceType {
        (self.0 & (0b11 << 12) >> 12) as PieceType
    }
}

#[inline]
pub fn square(file: File, rank: Rank) -> Square {
    assert!(file < 8);
    assert!(rank < 8);
    rank * 8 + file
}

#[inline]
pub fn rank(square: Square) -> usize {
    square / 8
}

#[inline]
pub fn file(square: Square) -> usize {
    square % 8
}

<commit_msg>Cosmetic change.<commit_after>// Basic types
pub type Color = usize; // 0 or 1
pub type File = usize;  // from 0 to 7
pub type Rank = usize;  // from 0 to 7
pub type Square = usize;  // from 0 to 63
pub type PieceType = usize;  // from 0 to 5
pub type MoveType = usize;  // from 0 to 3
pub type CastlingRights = [(bool, bool); 2];  // (King-side, Queen-side)

// Color
pub const WHITE: Color = 0;
pub const BLACK: Color = 1;

// Piece types
pub const QUEEN: PieceType = 0;
pub const ROOK: PieceType = 1;
pub const BISHOP: PieceType = 2;
pub const KNIGHT: PieceType = 3;
pub const KING: PieceType = 4;
pub const PAWN: PieceType = 5;

// Move types
pub const MOVE_NORMAL: MoveType = 0;
pub const MOVE_ENPASSANT: MoveType = 1;
pub const MOVE_PROMOTION: MoveType = 2;
pub const MOVE_CASTLING: MoveType = 3;

// Ranks
pub const RANK_1: Rank = 0;
pub const RANK_2: Rank = 1;
pub const RANK_3: Rank = 2;
pub const RANK_4: Rank = 3;
pub const RANK_5: Rank = 4;
pub const RANK_6: Rank = 5;
pub const RANK_7: Rank = 6;
pub const RANK_8: Rank = 7;

// Files
pub const FILE_A: Rank = 0;
pub const FILE_B: Rank = 1;
pub const FILE_C: Rank = 2;
pub const FILE_D: Rank = 3;
pub const FILE_E: Rank = 4;
pub const FILE_F: Rank = 5;
pub const FILE_G: Rank = 6;
pub const FILE_H: Rank = 7;

// Squares
pub const A1: Square = 0 + 0*8;
pub const B1: Square = 1 + 0*8;
pub const C1: Square = 2 + 0*8;
pub const D1: Square = 3 + 0*8;
pub const E1: Square = 4 + 0*8;
pub const F1: Square = 5 + 0*8;
pub const G1: Square = 6 + 0*8;
pub const H1: Square = 7 + 0*8;

pub const A2: Square = 0 + 1*8;
pub const B2: Square = 1 + 1*8;
pub const C2: Square = 2 + 1*8;
pub const D2: Square = 3 + 1*8;
pub const E2: Square = 4 + 1*8;
pub const F2: Square = 5 + 1*8;
pub const G2: Square = 6 + 1*8;
pub const H2: Square = 7 + 1*8;

pub const A3: Square = 0 + 2*8;
pub const B3: Square = 1 + 2*8;
pub const C3: Square = 2 + 2*8;
pub const D3: Square = 3 + 2*8;
pub const E3: Square = 4 + 2*8;
pub const F3: Square = 5 + 2*8;
pub const G3: Square = 6 + 2*8;
pub const H3: Square = 7 + 2*8;

pub const A4: Square = 0 + 3*8;
pub const B4: Square = 1 + 3*8;
pub const C4: Square = 2 + 3*8;
pub const D4: Square = 3 + 3*8;
pub const E4: Square = 4 + 3*8;
pub const F4: Square = 5 + 3*8;
pub const G4: Square = 6 + 3*8;
pub const H4: Square = 7 + 3*8;

pub const A5: Square = 0 + 4*8;
pub const B5: Square = 1 + 4*8;
pub const C5: Square = 2 + 4*8;
pub const D5: Square = 3 + 4*8;
pub const E5: Square = 4 + 4*8;
pub const F5: Square = 5 + 4*8;
pub const G5: Square = 6 + 4*8;
pub const H5: Square = 7 + 4*8;

pub const A6: Square = 0 + 5*8;
pub const B6: Square = 1 + 5*8;
pub const C6: Square = 2 + 5*8;
pub const D6: Square = 3 + 5*8;
pub const E6: Square = 4 + 5*8;
pub const F6: Square = 5 + 5*8;
pub const G6: Square = 6 + 5*8;
pub const H6: Square = 7 + 5*8;

pub const A7: Square = 0 + 6*8;
pub const B7: Square = 1 + 6*8;
pub const C7: Square = 2 + 6*8;
pub const D7: Square = 3 + 6*8;
pub const E7: Square = 4 + 6*8;
pub const F7: Square = 5 + 6*8;
pub const G7: Square = 6 + 6*8;
pub const H7: Square = 7 + 6*8;

pub const A8: Square = 0 + 7*8;
pub const B8: Square = 1 + 7*8;
pub const C8: Square = 2 + 7*8;
pub const D8: Square = 3 + 7*8;
pub const E8: Square = 4 + 7*8;
pub const F8: Square = 5 + 7*8;
pub const G8: Square = 6 + 7*8;
pub const H8: Square = 7 + 7*8;

pub struct Move(u16);

impl Move {
    #[inline]
    pub fn new(m_type: MoveType, orig: Square, dest: Square, pp_type: PieceType) -> Move {
        assert!(m_type <= 3);
        assert!(orig <= 63);
        assert!(dest <= 63);
        assert!(pp_type <= 3);
        Move( ((m_type << 14) | (pp_type << 12) | (orig << 6) | dest) as u16 )
    }

    #[inline]
    pub fn move_type(&self) -> MoveType {
        ((self.0 & (0b11 << 14)) >> 14) as MoveType
    }

    #[inline]
    pub fn orig_square(&self) -> Square {
        (self.0 & (0b111111 << 6) >> 6) as Square
    }

    #[inline]
    pub fn dest_square(&self) -> Square {
        (self.0 & 0b111111) as Square
    }

    #[inline]
    pub fn promotion_piece_type(&self) -> PieceType {
        (self.0 & (0b11 << 12) >> 12) as PieceType
    }
}

#[inline]
pub fn square(file: File, rank: Rank) -> Square {
    assert!(file < 8);
    assert!(rank < 8);
    rank * 8 + file
}

#[inline]
pub fn rank(square: Square) -> Rank {
    square / 8
}

#[inline]
pub fn file(square: Square) -> File {
    square % 8
}

<|endoftext|>


<commit_before>#![deny(warnings)]
#![allow(unstable)]

extern crate seqloq;

use std::sync::{Mutex, RwLock};
use std::default::Default;
use std::io::File;
use std::path::Path;

use seqloq::Seqloq;
use seqloq::tests::{TestArray, BenchMode, BenchRequest, ThreadSpec};
use seqloq::tests::{SeqloqPeek, reader_writer_test};

pub fn main() {
    // Infrequent writes
    let writers = ThreadSpec {
        qty: 3,
        pause: 2000,
        ..Default::default()
    };
    // Demanding readers
    let readers = ThreadSpec {
        qty: 200,
        pause: 0,
        ..Default::default()
    };

    macro_rules! bench_one {
        ($name:expr, $mutex:ident, $mode:ident) => ({
            let mut samples = vec![];

            {
                let bench = BenchRequest {
                    mode: BenchMode::$mode,
                    num_samples: 10_000,
                    samples: &mut samples,
                };
                reader_writer_test::<$mutex<TestArray>>(readers, writers,
                    Some(bench), false);
            }

            let mut out = File::create(&Path::new(concat!("target/", $name))).unwrap();
            for sample in samples.iter() {
                writeln!(&mut out, "{}", sample)
                    .unwrap();
            }
        })
    }

    bench_one!("mutex_read.dat", Mutex,  Reader);
    bench_one!("rwlock_read.dat", RwLock, Reader);
    bench_one!("seqloq_read.dat", Seqloq, Reader);
    bench_one!("seqloq-peek_read.dat", SeqloqPeek, Reader);

    bench_one!("mutex_write.dat", Mutex,  Writer);
    bench_one!("rwlock_write.dat", RwLock, Writer);
    bench_one!("seqloq_write.dat", Seqloq, Writer);
    bench_one!("seqloq-peek_write.dat", SeqloqPeek, Writer);
}
<commit_msg>Don't resize the samples vector in the benchmark<commit_after>#![deny(warnings)]
#![allow(unstable)]

extern crate seqloq;

use std::sync::{Mutex, RwLock};
use std::default::Default;
use std::io::File;
use std::path::Path;

use seqloq::Seqloq;
use seqloq::tests::{TestArray, BenchMode, BenchRequest, ThreadSpec};
use seqloq::tests::{SeqloqPeek, reader_writer_test};

const NUM_SAMPLES: u64 = 10_000;

pub fn main() {
    // Infrequent writes
    let writers = ThreadSpec {
        qty: 3,
        pause: 2000,
        ..Default::default()
    };
    // Demanding readers
    let readers = ThreadSpec {
        qty: 200,
        pause: 0,
        ..Default::default()
    };

    macro_rules! bench_one {
        ($name:expr, $mutex:ident, $mode:ident) => ({
            let mut samples = Vec::with_capacity(NUM_SAMPLES as usize);

            {
                let bench = BenchRequest {
                    mode: BenchMode::$mode,
                    num_samples: NUM_SAMPLES,
                    samples: &mut samples,
                };
                reader_writer_test::<$mutex<TestArray>>(readers, writers,
                    Some(bench), false);
            }

            let mut out = File::create(&Path::new(concat!("target/", $name))).unwrap();
            for sample in samples.iter() {
                writeln!(&mut out, "{}", sample)
                    .unwrap();
            }
        })
    }

    bench_one!("mutex_read.dat", Mutex,  Reader);
    bench_one!("rwlock_read.dat", RwLock, Reader);
    bench_one!("seqloq_read.dat", Seqloq, Reader);
    bench_one!("seqloq-peek_read.dat", SeqloqPeek, Reader);

    bench_one!("mutex_write.dat", Mutex,  Writer);
    bench_one!("rwlock_write.dat", RwLock, Writer);
    bench_one!("seqloq_write.dat", Seqloq, Writer);
    bench_one!("seqloq-peek_write.dat", SeqloqPeek, Writer);
}
<|endoftext|>