<commit_before>
extern crate rustc_serialize;
extern crate amqp;

use amqp::{ConsumerCallBackFn, Session, Table, Basic, Channel, Options};
use amqp::protocol;
use std::default::Default;
use rustc_serialize::json;

// for debugging
fn listen_discovery(channel: &mut Channel) {
    fn participant_discovered(channel: &mut Channel, deliver: protocol::basic::Deliver,
                            headers: protocol::basic::BasicProperties, body: Vec<u8>){
        let s = std::str::from_utf8(&body).unwrap();
        let info: ParticipantInfo = json::decode(s).unwrap();
        println!("MsgFlo participant discovered: {:?}", info);
        //println!("Deliver info: {:?}", deliver);
        //println!("Content headers: {:?}", headers);
        //println!("Content body: ", );
        channel.basic_ack(deliver.delivery_tag, false);
    }

    let queue_name = "fbp";
    let consumer_name = channel.basic_consume(participant_discovered as ConsumerCallBackFn, queue_name,
                                                "", false, false, false, false, Table::new());
}

#[derive(Debug, Default, RustcDecodable, RustcEncodable)]
struct ParticipantInfo {
    id: String, // unique name
    role: String ,// role participant has
    component: String, // component the participant is instance of
//   label: Option<String>, // (optional) short human-readable description
//    icon: Option<String>, // (optional)
}

/*
    inports: list of inports containing:
        id: port name
        queue: the message queue the process listens to
        type: port datatype, for example boolean
        options: queue options as specified by the message queue implementation
    outports: list of outports containing:
        id: port name
        queue: the message queue the process transmits to
        type: port datatype, for example boolean
        options: queue options as specified by the message queue implementation
*/

fn send_discovery(channel: &mut Channel) {
    let queue_name = "fbp"; // TODO: use an exchange istead, requires protocol change in msgflo

    let queue_declare = channel.queue_declare(queue_name, false, true, false, false, false, Table::new());
    let content_type = Some("application/json".to_string());
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };

    let info = ParticipantInfo {
        id: "part11".to_string(),
        role: "myrole".to_string(),
        component: "rust/First".to_string(),
    };

    let payload = json::encode(&info).unwrap();
    println!("sending: {}", payload);
    let res = channel.basic_publish("", queue_name, true, false, props, payload.into_bytes());
}

// for debugging
fn selftest() {
    let mut session = Session::new(Options{vhost: "/", .. Default::default()}).ok().expect("Can't create session");
    let mut channel = session.open_channel(1).expect("channel");

    send_discovery(&mut channel);
    listen_discovery(&mut channel);

    channel.start_consuming();

    let closed = channel.close(200, "Bye".to_string());
    session.close(200, "Good Bye".to_string());
}

fn main() {
    selftest();
}
<commit_msg>Also serialize in/outports<commit_after>
extern crate rustc_serialize;
extern crate amqp;

use amqp::{ConsumerCallBackFn, Session, Table, Basic, Channel, Options};
use amqp::protocol;
use std::default::Default;
use rustc_serialize::json;
use std::slice;

// for debugging
fn listen_discovery(channel: &mut Channel) {
    fn participant_discovered(channel: &mut Channel, deliver: protocol::basic::Deliver,
                            headers: protocol::basic::BasicProperties, body: Vec<u8>){
        let s = std::str::from_utf8(&body).unwrap();
        let info: ParticipantInfo = json::decode(s).unwrap();
        println!("MsgFlo participant discovered: {:?}", info);
        //println!("Deliver info: {:?}", deliver);
        //println!("Content headers: {:?}", headers);
        //println!("Content body: ", );
        channel.basic_ack(deliver.delivery_tag, false);
    }

    let queue_name = "fbp";
    let consumer_name = channel.basic_consume(participant_discovered as ConsumerCallBackFn, queue_name,
                                                "", false, false, false, false, Table::new());
}

#[derive(Debug, Default, RustcDecodable, RustcEncodable)]
struct ParticipantPort {
    id: String, // port name
    queue: String, // the associated message queue
    // FIXME: support. Is a keyword so needs some special handling   type: String, // datatype, ex: "boolean"
    // options: queue options as specified by the message queue implementation
}

#[derive(Debug, Default, RustcDecodable, RustcEncodable)]
struct ParticipantInfo {
    id: String, // unique name
    role: String ,// role participant has
    component: String, // component the participant is instance of
//   label: Option<String>, // (optional) short human-readable description
//    icon: Option<String>, // (optional)
    inports: Vec<ParticipantPort>,
    outports: Vec<ParticipantPort>,
}

fn send_discovery(channel: &mut Channel, info: &ParticipantInfo) {
    let queue_name = "fbp"; // TODO: use an exchange istead, requires protocol change in msgflo

    let queue_declare = channel.queue_declare(queue_name, false, true, false, false, false, Table::new());
    let content_type = Some("application/json".to_string());
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };

    let payload = json::encode(&info).unwrap();
    println!("sending: {}", payload);
    let res = channel.basic_publish("", queue_name, true, false, props, payload.into_bytes());
}

// for debugging
fn selftest() {
    let mut session = Session::new(Options{vhost: "/", .. Default::default()}).ok().expect("Can't create session");
    let mut channel = session.open_channel(1).expect("channel");

    let info = ParticipantInfo {
        id: "part11".to_string(),
        role: "myrole".to_string(),
        component: "rust/First".to_string(),
        inports: Vec::<ParticipantPort>::new(),
        outports: Vec::<ParticipantPort>::new(),
    };

    send_discovery(&mut channel, &info);
    listen_discovery(&mut channel);

    channel.start_consuming();

    let closed = channel.close(200, "Bye".to_string());
    session.close(200, "Good Bye".to_string());
}

fn main() {
    selftest();
}
<|endoftext|>


<commit_before>// (C)opyleft 2013 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#[desc = "A fast Bloom filter implementation."];
#[license = "BSD"];
#[link(name = "bloomfilter", vers = "0.1")];
#[crate_type = "lib"];
#[warn(non_camel_case_types, non_uppercase_statics, non_uppercase_statics, unnecessary_qualification, managed_heap_memory)];

extern mod extra;

use std::num;
use std::rand;
use std::rand::Rng;
use extra::bitv;

struct SipHashKey {
    k1: u64,
    k2: u64
}

impl SipHashKey {
    fn new(k1: u64, k2: u64) -> SipHashKey {
        SipHashKey {
            k1: k1,
            k2: k2
        }
    }

    fn new_random() -> SipHashKey {
        let mut rng = rand::task_rng();
        SipHashKey {
            k1: rand::Rand::rand(& mut rng),
            k2: rand::Rand::rand(& mut rng)
        }
    }
}

/// Bloom filter structure
pub struct Bloom {
    priv bitmap: bitv::Bitv,
    priv bitmap_bits: u64,
    priv k_num: uint,
    priv skeys: [SipHashKey, ..2]
}

impl Bloom {

/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: uint, items_count: uint) -> Bloom {
        assert!(bitmap_size > 0u && items_count > 0u);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::new(bitmap_bits as uint, false);
        let skeys = [ SipHashKey::new_random(), SipHashKey::new_random() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            skeys: skeys
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: uint, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: uint, fp_p: f64) -> uint {
        assert!(items_count > 0u);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = num::ln(2.0f64);
        let log2_2 = log2 * log2;
        ((items_count as f64) * num::ln(fp_p) / (-8.0 * log2_2)).ceil() as uint
    }

/// Record the presence of an item.
    pub fn set<T: Hash>(& mut self, item: T) {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T: Hash>(&self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T: Hash>(&mut self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: uint) -> uint {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * num::ln(2.0f64).ceil()) as uint;
        num::max(k_num, 1)
    }

    fn bloom_hash<T: Hash>(&self, hashes: & mut [u64, ..2],
                  item: &T, k_i: uint) -> u64 {
        if k_i < 2 {
            let skey = self.skeys[k_i];
            let hash = (*item).hash_keyed(skey.k1, skey.k2);
            hashes[k_i] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key = &rand::task_rng().gen_ascii_str(16u);
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key = &rand::task_rng().gen_ascii_str(16u);
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<commit_msg>Remove unused method<commit_after>// (C)opyleft 2013 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#[desc = "A fast Bloom filter implementation."];
#[license = "BSD"];
#[link(name = "bloomfilter", vers = "0.1")];
#[crate_type = "lib"];
#[warn(non_camel_case_types, non_uppercase_statics, non_uppercase_statics, unnecessary_qualification, managed_heap_memory)];

extern mod extra;

use std::num;
use std::rand;
use std::rand::Rng;
use extra::bitv;

struct SipHashKey {
    k1: u64,
    k2: u64
}

impl SipHashKey {
    fn new_random() -> SipHashKey {
        let mut rng = rand::task_rng();
        SipHashKey {
            k1: rand::Rand::rand(& mut rng),
            k2: rand::Rand::rand(& mut rng)
        }
    }
}

/// Bloom filter structure
pub struct Bloom {
    priv bitmap: bitv::Bitv,
    priv bitmap_bits: u64,
    priv k_num: uint,
    priv skeys: [SipHashKey, ..2]
}

impl Bloom {

/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: uint, items_count: uint) -> Bloom {
        assert!(bitmap_size > 0u && items_count > 0u);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::new(bitmap_bits as uint, false);
        let skeys = [ SipHashKey::new_random(), SipHashKey::new_random() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            skeys: skeys
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: uint, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: uint, fp_p: f64) -> uint {
        assert!(items_count > 0u);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = num::ln(2.0f64);
        let log2_2 = log2 * log2;
        ((items_count as f64) * num::ln(fp_p) / (-8.0 * log2_2)).ceil() as uint
    }

/// Record the presence of an item.
    pub fn set<T: Hash>(& mut self, item: T) {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T: Hash>(&self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T: Hash>(&mut self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: uint) -> uint {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * num::ln(2.0f64).ceil()) as uint;
        num::max(k_num, 1)
    }

    fn bloom_hash<T: Hash>(&self, hashes: & mut [u64, ..2],
                  item: &T, k_i: uint) -> u64 {
        if k_i < 2 {
            let skey = self.skeys[k_i];
            let hash = (*item).hash_keyed(skey.k1, skey.k2);
            hashes[k_i] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key = &rand::task_rng().gen_ascii_str(16u);
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key = &rand::task_rng().gen_ascii_str(16u);
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<|endoftext|>


<commit_before>// (C)opyleft 2013,2014 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#![desc = "A fast Bloom filter implementation."]
#![license = "BSD"]
#![crate_name="bloomfilter"]
#![crate_type = "rlib"]
#![warn(non_camel_case_types, non_uppercase_statics, unnecessary_qualification, managed_heap_memory)]

extern crate collections;

use std::cmp;
use std::f64;
use std::hash::Hash;
use std::hash::sip::SipHasher;
use std::hash::Hasher;
use std::rand;
use collections::bitv;

#[cfg(test)]
use std::rand::Rng;

/// Bloom filter structure
pub struct Bloom {
    bitmap: bitv::Bitv,
    bitmap_bits: u64,
    k_num: uint,
    sips: [SipHasher, ..2]
}

impl Bloom {
/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: uint, items_count: uint) -> Bloom {
        assert!(bitmap_size > 0u && items_count > 0u);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::new(bitmap_bits as uint, false);
        let sips = [ Bloom::sip_new(), Bloom::sip_new() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            sips: sips
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: uint, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: uint, fp_p: f64) -> uint {
        assert!(items_count > 0u);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = f64::consts::LN_2;
        let log2_2 = log2 * log2;
        ((items_count as f64) * fp_p.ln() / (-8.0 * log2_2)).ceil() as uint
    }

/// Record the presence of an item.
    pub fn set<T: Hash>(& mut self, item: T) {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T: Hash>(&self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T: Hash>(&mut self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: uint) -> uint {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * 2.0f64.ln()).ceil() as uint;
        cmp::max(k_num, 1)
    }

    fn bloom_hash<T: Hash>(&self, hashes: & mut [u64, ..2],
                  item: &T, k_i: uint) -> u64 {
        if k_i < 2 {
            let sip = self.sips[k_i];
            let hash = sip.hash(item);
            hashes[k_i] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }

    fn sip_new() -> SipHasher {
        let mut rng = rand::task_rng();
        SipHasher::new_with_keys(rand::Rand::rand(& mut rng),
                                 rand::Rand::rand(& mut rng))
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::task_rng().gen_iter::<u8>().take(16u).collect();
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::task_rng().gen_iter::<u8>().take(16u).collect();
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<commit_msg>Use Bitv::with_capacity()<commit_after>// (C)opyleft 2013,2014 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#![desc = "A fast Bloom filter implementation."]
#![license = "BSD"]
#![crate_name="bloomfilter"]
#![crate_type = "rlib"]
#![warn(non_camel_case_types, non_uppercase_statics, unnecessary_qualification, managed_heap_memory)]

extern crate collections;

use std::cmp;
use std::f64;
use std::hash::Hash;
use std::hash::sip::SipHasher;
use std::hash::Hasher;
use std::rand;
use collections::bitv;

#[cfg(test)]
use std::rand::Rng;

/// Bloom filter structure
pub struct Bloom {
    bitmap: bitv::Bitv,
    bitmap_bits: u64,
    k_num: uint,
    sips: [SipHasher, ..2]
}

impl Bloom {
/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: uint, items_count: uint) -> Bloom {
        assert!(bitmap_size > 0u && items_count > 0u);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::with_capacity(bitmap_bits as uint, false);
        let sips = [ Bloom::sip_new(), Bloom::sip_new() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            sips: sips
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: uint, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: uint, fp_p: f64) -> uint {
        assert!(items_count > 0u);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = f64::consts::LN_2;
        let log2_2 = log2 * log2;
        ((items_count as f64) * fp_p.ln() / (-8.0 * log2_2)).ceil() as uint
    }

/// Record the presence of an item.
    pub fn set<T: Hash>(& mut self, item: T) {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T: Hash>(&self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T: Hash>(&mut self, item: T) -> bool {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in range(0u, self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as uint;
            if self.bitmap.get(bit_offset) == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: uint) -> uint {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * 2.0f64.ln()).ceil() as uint;
        cmp::max(k_num, 1)
    }

    fn bloom_hash<T: Hash>(&self, hashes: & mut [u64, ..2],
                  item: &T, k_i: uint) -> u64 {
        if k_i < 2 {
            let sip = self.sips[k_i];
            let hash = sip.hash(item);
            hashes[k_i] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }

    fn sip_new() -> SipHasher {
        let mut rng = rand::task_rng();
        SipHasher::new_with_keys(rand::Rand::rand(& mut rng),
                                 rand::Rand::rand(& mut rng))
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::task_rng().gen_iter::<u8>().take(16u).collect();
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::task_rng().gen_iter::<u8>().take(16u).collect();
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<|endoftext|>


<commit_before>// Copyright 2016 `multipart` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.
use mock::{ClientRequest, HttpBuffer};

use server::{MultipartField, MultipartData, ReadEntry};

use mime::Mime;

use rand::{self, Rng};

use std::collections::{HashMap, HashSet};
use std::fmt;
use std::io::prelude::*;
use std::io::Cursor;
use std::iter::{self, FromIterator};

const MIN_FIELDS: usize = 1;
const MAX_FIELDS: usize = 3;

const MIN_LEN: usize = 2;
const MAX_LEN: usize = 5;
const MAX_DASHES: usize = 2;

fn collect_rand<C: FromIterator<T>, T, F: FnMut() -> T>(mut gen: F) -> C {
    (0 .. rand::thread_rng().gen_range(MIN_FIELDS, MAX_FIELDS))
        .map(|_| gen()).collect()
}

macro_rules! expect_fmt (
    ($val:expr, $($args:tt)*) => (
        match $val {
            Some(val) => val,
            None => panic!($($args)*),
        }
    );
);

#[derive(Debug)]
struct TestFields {
    texts: HashMap<String, String>,
    files: HashMap<String, HashSet<FileEntry>>,
}

impl TestFields {
    fn gen() -> Self {
        TestFields {
            texts: collect_rand(|| (gen_string(), gen_string())),
            files: collect_rand(|| (gen_string(), FileEntry::gen_many())),
        }
    }

    fn check_field<M: ReadEntry>(&mut self, field: &mut MultipartField<M>) {
        match field.data {
            MultipartData::Text(ref text) => {
                let test_text = expect_fmt!(self.texts.remove(&field.name),
                    "Got text field that wasn't in original dataset: {:?} : {:?} ",
                    field.name, text.text
                );

                assert!(
                    text.text == test_text,
                    "Unexpected data for field {:?}: Expected {:?}, got {:?}",
                    field.name, test_text, text.text
                );
            },
            MultipartData::File(ref mut file) => {
                let mut bytes = Vec::with_capacity(MAX_LEN);
                file.read_to_end(&mut bytes).unwrap();

                let curr_file = FileEntry {
                    content_type: file.content_type.clone(),
                    filename: file.filename.take(),
                    data: PrintHex(bytes),
                };

                let files_empty = {
                    let mut files = expect_fmt!(self.files.get_mut(&field.name),
                    "Got file field that wasn't in original dataset: {:?} : {:?}",
                    field.name, curr_file);

                    assert!(files.remove(&curr_file), "Unexpected data for file field {:?}: {:?}",
                        field.name, curr_file);

                    files.is_empty()
                };

                if files_empty {
                    let _ = self.files.remove(&field.name);
                }
            },
        }
    }

    fn assert_is_empty(&self) {
        assert!(self.texts.is_empty(), "Text fields were not exhausted! Text fields: {:?}", self.texts);
        assert!(self.files.is_empty(), "File fields were not exhausted! File fields: {:?}", self.files);
    }
}

#[derive(Debug, Hash, PartialEq, Eq)]
struct FileEntry {
    content_type: Mime,
    filename: Option<String>,
    data: PrintHex,
}

impl FileEntry {
    fn gen_many() -> HashSet<Self> {
        collect_rand(Self::gen)
    }

    fn gen() -> Self {
        let filename = match gen_bool() {
            true => Some(gen_string()),
            false => None,
        };

        FileEntry {
            content_type: rand_mime(),
            filename: filename,
            data: PrintHex(gen_bytes())
        }
    }

    fn filename(&self) -> Option<&str> {
        self.filename.as_ref().map(|s| &**s)
    }
}

#[derive(Hash, PartialEq, Eq)]
struct PrintHex(Vec<u8>);

impl fmt::Debug for PrintHex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "["));

        let mut written = false;

        for byte in &self.0 {
            try!(write!(f, "{:X}", byte));

            if written {
                try!(write!(f, ", "));
            }

            written = true;
        }

        write!(f, "]")
    }
}

macro_rules! do_test (
    ($client_test:ident, $server_test:ident) => (
        let _ = ::env_logger::init();

        info!("Client Test: {:?} Server Test: {:?}", stringify!($client_test),
              stringify!($server_test));

        let mut test_fields = TestFields::gen();

        trace!("Fields for test: {:?}", test_fields);

        let buf = $client_test(&test_fields);

        trace!(
            "\n==Test Buffer Begin==\n{}\n==Test Buffer End==",
            String::from_utf8_lossy(&buf.buf)
        );

        $server_test(buf, &mut test_fields);

        test_fields.assert_is_empty();
    );
);

#[test]
fn reg_client_reg_server() {
    do_test!(test_client, test_server);
}

#[test]
fn reg_client_entry_server() {
    do_test!(test_client, test_server_entry_api);
}

#[test]
fn lazy_client_reg_server() {
    do_test!(test_client_lazy, test_server);
}

#[test]
fn lazy_client_entry_server() {
    do_test!(test_client_lazy, test_server_entry_api);
}

mod extended {
    use super::*;

    use std::time::Instant;

    const TIME_LIMIT_SECS: u64 = 300;

    #[test]
    #[ignore]
    fn reg_client_reg_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client, test_server);
        }
    }

    #[test]
    #[ignore]
    fn reg_client_entry_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client, test_server_entry_api);
        }
    }

    #[test]
    #[ignore]
    fn lazy_client_reg_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client_lazy, test_server);
        }
    }

    #[test]
    #[ignore]
    fn lazy_client_entry_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client_lazy, test_server_entry_api);
        }
    }
}

fn gen_bool() -> bool {
    rand::thread_rng().gen()
}

fn gen_string() -> String {
    let mut rng_1 = rand::thread_rng();
    let mut rng_2 = rand::thread_rng();

    let str_len_1 = rng_1.gen_range(MIN_LEN, MAX_LEN + 1);
    let str_len_2 = rng_2.gen_range(MIN_LEN, MAX_LEN + 1);
    let num_dashes = rng_1.gen_range(0, MAX_DASHES + 1);

    rng_1.gen_ascii_chars().take(str_len_1)
        .chain(iter::repeat('-').take(num_dashes))
        .chain(rng_2.gen_ascii_chars().take(str_len_2))
        .collect()
}

fn gen_bytes() -> Vec<u8> {
    gen_string().into_bytes()
}

fn test_client(test_fields: &TestFields) -> HttpBuffer {
    use client::Multipart;

    let request = ClientRequest::default();

    let mut test_files = test_fields.files.iter();

    let mut multipart = Multipart::from_request(request).unwrap();

    // Intersperse file fields amongst text fields
    for (name, text) in &test_fields.texts {
        if let Some((file_name, files)) = test_files.next() {
            for file in files {
                multipart.write_stream(file_name, &mut &*file.data.0, file.filename(),
                                       Some(file.content_type.clone())).unwrap();
            }
        }

        multipart.write_text(name, text).unwrap();
    }

    // Write remaining files
    for (file_name, files) in test_files {
        for file in files {
            multipart.write_stream(file_name, &mut &*file.data.0, file.filename(),
                                   Some(file.content_type.clone())).unwrap();
        }
    }

    multipart.send().unwrap()
}

fn test_client_lazy(test_fields: &TestFields) -> HttpBuffer {
    use client::lazy::Multipart;

    let mut multipart = Multipart::new();

    let mut test_files = test_fields.files.iter();

    for (name, text) in &test_fields.texts {
        if let Some((file_name, files)) = test_files.next() {
            for file in files {
                multipart.add_stream(&**file_name, Cursor::new(&file.data.0), file.filename(),
                                     Some(file.content_type.clone()));
            }
        }

        multipart.add_text(&**name, &**text);
    }

    for (file_name, files) in test_files {
        for file in files {
            multipart.add_stream(&**file_name, Cursor::new(&file.data.0), file.filename(),
                                 Some(file.content_type.clone()));
        }
    }

    let mut prepared = multipart.prepare_threshold(None).unwrap();

    let mut buf = Vec::new();

    let boundary = prepared.boundary().to_owned();
    let content_len = prepared.content_len();

    prepared.read_to_end(&mut buf).unwrap();

    HttpBuffer::with_buf(buf, boundary, content_len)
}

fn test_server(buf: HttpBuffer, fields: &mut TestFields) {
    use server::Multipart;

    let server_buf = buf.for_server();

    if let Some(content_len) = server_buf.content_len {
        assert!(content_len == server_buf.data.len() as u64, "Supplied content_len different from actual");
    }

    let mut multipart = Multipart::from_request(server_buf)
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    while let Some(mut field) = multipart.read_entry_mut().unwrap_opt() {
        fields.check_field(&mut field);
    }
}

fn test_server_entry_api(buf: HttpBuffer, fields: &mut TestFields) {
    use server::Multipart;

    let server_buf = buf.for_server();

    if let Some(content_len) = server_buf.content_len {
        assert!(content_len == server_buf.data.len() as u64, "Supplied content_len different from actual");
    }

    let multipart = Multipart::from_request(server_buf)
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    let mut entry = multipart.into_entry().expect_alt("Expected entry, got none", "Error reading entry");
    fields.check_field(&mut entry);

    while let Some(entry_) = entry.next_entry().unwrap_opt() {
        entry = entry_;
        fields.check_field(&mut entry);
    }
}

fn rand_mime() -> Mime {
    rand::thread_rng().choose(&[
        // TODO: fill this out, preferably with variants that may be hard to parse
        // i.e. containing hyphens, mainly
        mime!(Application/OctetStream),
        mime!(Text/Plain),
        mime!(Image/Png),
    ]).unwrap().clone()
}<commit_msg>fix tests for rust < 1.15.0<commit_after>// Copyright 2016 `multipart` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.
use mock::{ClientRequest, HttpBuffer};

use server::{MultipartField, MultipartData, ReadEntry};

use mime::Mime;

use rand::{self, Rng};

use std::collections::{HashMap, HashSet};
use std::fmt;
use std::io::prelude::*;
use std::io::Cursor;
use std::iter::{self, FromIterator};

const MIN_FIELDS: usize = 1;
const MAX_FIELDS: usize = 3;

const MIN_LEN: usize = 2;
const MAX_LEN: usize = 5;
const MAX_DASHES: usize = 2;

fn collect_rand<C: FromIterator<T>, T, F: FnMut() -> T>(mut gen: F) -> C {
    (0 .. rand::thread_rng().gen_range(MIN_FIELDS, MAX_FIELDS))
        .map(|_| gen()).collect()
}

macro_rules! expect_fmt (
    ($val:expr, $($args:tt)*) => (
        match $val {
            Some(val) => val,
            None => panic!($($args)*),
        }
    );
);

#[derive(Debug)]
struct TestFields {
    texts: HashMap<String, String>,
    files: HashMap<String, HashSet<FileEntry>>,
}

impl TestFields {
    fn gen() -> Self {
        TestFields {
            texts: collect_rand(|| (gen_string(), gen_string())),
            files: collect_rand(|| (gen_string(), FileEntry::gen_many())),
        }
    }

    fn check_field<M: ReadEntry>(&mut self, field: &mut MultipartField<M>) {
        match field.data {
            MultipartData::Text(ref text) => {
                let test_text = expect_fmt!(self.texts.remove(&field.name),
                    "Got text field that wasn't in original dataset: {:?} : {:?} ",
                    field.name, text.text
                );

                assert!(
                    text.text == test_text,
                    "Unexpected data for field {:?}: Expected {:?}, got {:?}",
                    field.name, test_text, text.text
                );
            },
            MultipartData::File(ref mut file) => {
                let mut bytes = Vec::with_capacity(MAX_LEN);
                file.read_to_end(&mut bytes).unwrap();

                let curr_file = FileEntry {
                    content_type: file.content_type.clone(),
                    filename: file.filename.take(),
                    data: PrintHex(bytes),
                };

                let files_empty = {
                    let mut files = expect_fmt!(self.files.get_mut(&field.name),
                    "Got file field that wasn't in original dataset: {:?} : {:?}",
                    field.name, curr_file);

                    assert!(files.remove(&curr_file), "Unexpected data for file field {:?}: {:?}",
                        field.name, curr_file);

                    files.is_empty()
                };

                if files_empty {
                    let _ = self.files.remove(&field.name);
                }
            },
        }
    }

    fn assert_is_empty(&self) {
        assert!(self.texts.is_empty(), "Text fields were not exhausted! Text fields: {:?}", self.texts);
        assert!(self.files.is_empty(), "File fields were not exhausted! File fields: {:?}", self.files);
    }
}

#[derive(Debug, Hash, PartialEq, Eq)]
struct FileEntry {
    content_type: Mime,
    filename: Option<String>,
    data: PrintHex,
}

impl FileEntry {
    fn gen_many() -> HashSet<Self> {
        collect_rand(Self::gen)
    }

    fn gen() -> Self {
        let filename = match gen_bool() {
            true => Some(gen_string()),
            false => None,
        };

        FileEntry {
            content_type: rand_mime(),
            filename: filename,
            data: PrintHex(gen_bytes())
        }
    }

    fn filename(&self) -> Option<&str> {
        self.filename.as_ref().map(|s| &**s)
    }
}

#[derive(Hash, PartialEq, Eq)]
struct PrintHex(Vec<u8>);

impl fmt::Debug for PrintHex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "["));

        let mut written = false;

        for byte in &self.0 {
            try!(write!(f, "{:X}", byte));

            if written {
                try!(write!(f, ", "));
            }

            written = true;
        }

        write!(f, "]")
    }
}

macro_rules! do_test (
    ($client_test:ident, $server_test:ident) => (
        let _ = ::env_logger::init();

        info!("Client Test: {:?} Server Test: {:?}", stringify!($client_test),
              stringify!($server_test));

        let mut test_fields = TestFields::gen();

        trace!("Fields for test: {:?}", test_fields);

        let buf = $client_test(&test_fields);

        trace!(
            "\n==Test Buffer Begin==\n{}\n==Test Buffer End==",
            String::from_utf8_lossy(&buf.buf)
        );

        $server_test(buf, &mut test_fields);

        test_fields.assert_is_empty();
    );
);

#[test]
fn reg_client_reg_server() {
    do_test!(test_client, test_server);
}

#[test]
fn reg_client_entry_server() {
    do_test!(test_client, test_server_entry_api);
}

#[test]
fn lazy_client_reg_server() {
    do_test!(test_client_lazy, test_server);
}

#[test]
fn lazy_client_entry_server() {
    do_test!(test_client_lazy, test_server_entry_api);
}

mod extended {
    use super::{test_client, test_server, test_server_entry_api, test_client_lazy, TestFields};

    use std::time::Instant;

    const TIME_LIMIT_SECS: u64 = 300;

    #[test]
    #[ignore]
    fn reg_client_reg_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client, test_server);
        }
    }

    #[test]
    #[ignore]
    fn reg_client_entry_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client, test_server_entry_api);
        }
    }

    #[test]
    #[ignore]
    fn lazy_client_reg_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client_lazy, test_server);
        }
    }

    #[test]
    #[ignore]
    fn lazy_client_entry_server() {
        let started = Instant::now();

        while started.elapsed().as_secs() < TIME_LIMIT_SECS {
            do_test!(test_client_lazy, test_server_entry_api);
        }
    }
}

fn gen_bool() -> bool {
    rand::thread_rng().gen()
}

fn gen_string() -> String {
    let mut rng_1 = rand::thread_rng();
    let mut rng_2 = rand::thread_rng();

    let str_len_1 = rng_1.gen_range(MIN_LEN, MAX_LEN + 1);
    let str_len_2 = rng_2.gen_range(MIN_LEN, MAX_LEN + 1);
    let num_dashes = rng_1.gen_range(0, MAX_DASHES + 1);

    rng_1.gen_ascii_chars().take(str_len_1)
        .chain(iter::repeat('-').take(num_dashes))
        .chain(rng_2.gen_ascii_chars().take(str_len_2))
        .collect()
}

fn gen_bytes() -> Vec<u8> {
    gen_string().into_bytes()
}

fn test_client(test_fields: &TestFields) -> HttpBuffer {
    use client::Multipart;

    let request = ClientRequest::default();

    let mut test_files = test_fields.files.iter();

    let mut multipart = Multipart::from_request(request).unwrap();

    // Intersperse file fields amongst text fields
    for (name, text) in &test_fields.texts {
        if let Some((file_name, files)) = test_files.next() {
            for file in files {
                multipart.write_stream(file_name, &mut &*file.data.0, file.filename(),
                                       Some(file.content_type.clone())).unwrap();
            }
        }

        multipart.write_text(name, text).unwrap();
    }

    // Write remaining files
    for (file_name, files) in test_files {
        for file in files {
            multipart.write_stream(file_name, &mut &*file.data.0, file.filename(),
                                   Some(file.content_type.clone())).unwrap();
        }
    }

    multipart.send().unwrap()
}

fn test_client_lazy(test_fields: &TestFields) -> HttpBuffer {
    use client::lazy::Multipart;

    let mut multipart = Multipart::new();

    let mut test_files = test_fields.files.iter();

    for (name, text) in &test_fields.texts {
        if let Some((file_name, files)) = test_files.next() {
            for file in files {
                multipart.add_stream(&**file_name, Cursor::new(&file.data.0), file.filename(),
                                     Some(file.content_type.clone()));
            }
        }

        multipart.add_text(&**name, &**text);
    }

    for (file_name, files) in test_files {
        for file in files {
            multipart.add_stream(&**file_name, Cursor::new(&file.data.0), file.filename(),
                                 Some(file.content_type.clone()));
        }
    }

    let mut prepared = multipart.prepare_threshold(None).unwrap();

    let mut buf = Vec::new();

    let boundary = prepared.boundary().to_owned();
    let content_len = prepared.content_len();

    prepared.read_to_end(&mut buf).unwrap();

    HttpBuffer::with_buf(buf, boundary, content_len)
}

fn test_server(buf: HttpBuffer, fields: &mut TestFields) {
    use server::Multipart;

    let server_buf = buf.for_server();

    if let Some(content_len) = server_buf.content_len {
        assert!(content_len == server_buf.data.len() as u64, "Supplied content_len different from actual");
    }

    let mut multipart = Multipart::from_request(server_buf)
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    while let Some(mut field) = multipart.read_entry_mut().unwrap_opt() {
        fields.check_field(&mut field);
    }
}

fn test_server_entry_api(buf: HttpBuffer, fields: &mut TestFields) {
    use server::Multipart;

    let server_buf = buf.for_server();

    if let Some(content_len) = server_buf.content_len {
        assert!(content_len == server_buf.data.len() as u64, "Supplied content_len different from actual");
    }

    let multipart = Multipart::from_request(server_buf)
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    let mut entry = multipart.into_entry().expect_alt("Expected entry, got none", "Error reading entry");
    fields.check_field(&mut entry);

    while let Some(entry_) = entry.next_entry().unwrap_opt() {
        entry = entry_;
        fields.check_field(&mut entry);
    }
}

fn rand_mime() -> Mime {
    rand::thread_rng().choose(&[
        // TODO: fill this out, preferably with variants that may be hard to parse
        // i.e. containing hyphens, mainly
        mime!(Application/OctetStream),
        mime!(Text/Plain),
        mime!(Image/Png),
    ]).unwrap().clone()
}
<|endoftext|>


<commit_before>use crate::{t, VERSION};
use std::fmt::Write as _;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{
    env, fmt, fs,
    io::{self, Write},
};

#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Profile {
    Compiler,
    Codegen,
    Library,
    User,
}

impl Profile {
    fn include_path(&self, src_path: &Path) -> PathBuf {
        PathBuf::from(format!("{}/src/bootstrap/defaults/config.{}.toml", src_path.display(), self))
    }

    pub fn all() -> impl Iterator<Item = Self> {
        use Profile::*;
        // N.B. these are ordered by how they are displayed, not alphabetically
        [Library, Compiler, Codegen, User].iter().copied()
    }

    pub fn purpose(&self) -> String {
        use Profile::*;
        match self {
            Library => "Contribute to the standard library",
            Compiler => "Contribute to the compiler or rustdoc",
            Codegen => "Contribute to the compiler, and also modify LLVM or codegen",
            User => "Install Rust from source",
        }
        .to_string()
    }

    pub fn all_for_help(indent: &str) -> String {
        let mut out = String::new();
        for choice in Profile::all() {
            writeln!(&mut out, "{}{}", indent, choice).unwrap();
        }
        out
    }
}

impl FromStr for Profile {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "lib" | "library" => Ok(Profile::Library),
            "compiler" | "rustdoc" => Ok(Profile::Compiler),
            "llvm" | "codegen" => Ok(Profile::Codegen),
            "maintainer" | "user" => Ok(Profile::User),
            _ => Err(format!("unknown profile: '{}'", s)),
        }
    }
}

impl fmt::Display for Profile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Profile::Compiler => write!(f, "compiler"),
            Profile::Codegen => write!(f, "codegen"),
            Profile::Library => write!(f, "library"),
            Profile::User => write!(f, "user"),
        }
    }
}

pub fn setup(src_path: &Path, profile: Profile) {
    let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);

    if cfg_file.as_ref().map_or(false, |f| f.exists()) {
        let file = cfg_file.unwrap();
        println!(
            "error: you asked `x.py` to setup a new config file, but one already exists at `{}`",
            file.display()
        );
        println!("help: try adding `profile = \"{}\"` at the top of {}", profile, file.display());
        println!(
            "note: this will use the configuration in {}",
            profile.include_path(src_path).display()
        );
        std::process::exit(1);
    }

    let path = cfg_file.unwrap_or_else(|| src_path.join("config.toml"));
    let settings = format!(
        "# Includes one of the default files in src/bootstrap/defaults\n\
    profile = \"{}\"\n\
    changelog-seen = {}\n",
        profile, VERSION
    );
    t!(fs::write(path, settings));

    let include_path = profile.include_path(src_path);
    println!("`x.py` will now use the configuration at {}", include_path.display());

    let suggestions = match profile {
        Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..],
        Profile::Library => &["check", "build", "test library/std", "doc"],
        Profile::User => &["dist", "build"],
    };

    println!();

    t!(install_git_hook_maybe(src_path));

    println!();

    println!("To get started, try one of the following commands:");
    for cmd in suggestions {
        println!("- `x.py {}`", cmd);
    }

    if profile != Profile::User {
        println!(
            "For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html"
        );
    }
}

// Used to get the path for `Subcommand::Setup`
pub fn interactive_path() -> io::Result<Profile> {
    fn abbrev_all() -> impl Iterator<Item = (String, Profile)> {
        ('a'..).map(|c| c.to_string()).zip(Profile::all())
    }

    fn parse_with_abbrev(input: &str) -> Result<Profile, String> {
        let input = input.trim().to_lowercase();
        for (letter, profile) in abbrev_all() {
            if input == letter {
                return Ok(profile);
            }
        }
        input.parse()
    }

    let mut input = String::new();
    println!("Welcome to the Rust project! What do you want to do with x.py?");
    for (letter, profile) in abbrev_all() {
        println!("{}) {}", letter, profile.purpose());
    }
    let template = loop {
        print!(
            "Please choose one ({}): ",
            abbrev_all().map(|(l, _)| l).collect::<Vec<_>>().join("/")
        );
        io::stdout().flush()?;
        io::stdin().read_line(&mut input)?;
        break match parse_with_abbrev(&input) {
            Ok(profile) => profile,
            Err(err) => {
                println!("error: {}", err);
                println!("note: press Ctrl+C to exit");
                continue;
            }
        };
    };
    Ok(template)
}

// install a git hook to automatically run tidy --bless, if they want
fn install_git_hook_maybe(src_path: &Path) -> io::Result<()> {
    let mut input = String::new();
    println!(
        "Rust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality.
If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` on each commit
to ensure your code is up to par. If you decide later that this behavior is undesirable,
simply delete the `pre-commit` file from .git/hooks."
    );

    let should_install = loop {
        print!("Would you like to install the git hook?: [y/N] ");
        io::stdout().flush()?;
        input.clear();
        io::stdin().read_line(&mut input)?;
        break match input.trim().to_lowercase().as_str() {
            "y" | "yes" => true,
            "n" | "no" | "" => false,
            _ => {
                println!("error: unrecognized option '{}'", input.trim());
                println!("note: press Ctrl+C to exit");
                continue;
            }
        };
    };

    Ok(if should_install {
        let src = src_path.join("src").join("etc").join("pre-commit.sh");
        let dst = src_path.join(".git").join("hooks").join("pre-commit");
        match fs::hard_link(src, dst) {
            Err(e) => println!(
                "x.py encountered an error -- do you already have the git hook installed?\n{}",
                e
            ),
            Ok(_) => println!("Linked `src/etc/pre-commit.sh` to `.git/hooks/pre-commit`"),
        };
    } else {
        println!("Ok, skipping installation!");
    })
}
<commit_msg>x.py: setup: Offer keywords in interactive prompt<commit_after>use crate::{t, VERSION};
use std::fmt::Write as _;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{
    env, fmt, fs,
    io::{self, Write},
};

#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Profile {
    Compiler,
    Codegen,
    Library,
    User,
}

impl Profile {
    fn include_path(&self, src_path: &Path) -> PathBuf {
        PathBuf::from(format!("{}/src/bootstrap/defaults/config.{}.toml", src_path.display(), self))
    }

    pub fn all() -> impl Iterator<Item = Self> {
        use Profile::*;
        // N.B. these are ordered by how they are displayed, not alphabetically
        [Library, Compiler, Codegen, User].iter().copied()
    }

    pub fn purpose(&self) -> String {
        use Profile::*;
        match self {
            Library => "Contribute to the standard library",
            Compiler => "Contribute to the compiler or rustdoc",
            Codegen => "Contribute to the compiler, and also modify LLVM or codegen",
            User => "Install Rust from source",
        }
        .to_string()
    }

    pub fn all_for_help(indent: &str) -> String {
        let mut out = String::new();
        for choice in Profile::all() {
            writeln!(&mut out, "{}{}: {}", indent, choice, choice.purpose()).unwrap();
        }
        out
    }
}

impl FromStr for Profile {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "lib" | "library" => Ok(Profile::Library),
            "compiler" | "rustdoc" => Ok(Profile::Compiler),
            "llvm" | "codegen" => Ok(Profile::Codegen),
            "maintainer" | "user" => Ok(Profile::User),
            _ => Err(format!("unknown profile: '{}'", s)),
        }
    }
}

impl fmt::Display for Profile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Profile::Compiler => write!(f, "compiler"),
            Profile::Codegen => write!(f, "codegen"),
            Profile::Library => write!(f, "library"),
            Profile::User => write!(f, "user"),
        }
    }
}

pub fn setup(src_path: &Path, profile: Profile) {
    let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);

    if cfg_file.as_ref().map_or(false, |f| f.exists()) {
        let file = cfg_file.unwrap();
        println!(
            "error: you asked `x.py` to setup a new config file, but one already exists at `{}`",
            file.display()
        );
        println!("help: try adding `profile = \"{}\"` at the top of {}", profile, file.display());
        println!(
            "note: this will use the configuration in {}",
            profile.include_path(src_path).display()
        );
        std::process::exit(1);
    }

    let path = cfg_file.unwrap_or_else(|| src_path.join("config.toml"));
    let settings = format!(
        "# Includes one of the default files in src/bootstrap/defaults\n\
    profile = \"{}\"\n\
    changelog-seen = {}\n",
        profile, VERSION
    );
    t!(fs::write(path, settings));

    let include_path = profile.include_path(src_path);
    println!("`x.py` will now use the configuration at {}", include_path.display());

    let suggestions = match profile {
        Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..],
        Profile::Library => &["check", "build", "test library/std", "doc"],
        Profile::User => &["dist", "build"],
    };

    println!();

    t!(install_git_hook_maybe(src_path));

    println!();

    println!("To get started, try one of the following commands:");
    for cmd in suggestions {
        println!("- `x.py {}`", cmd);
    }

    if profile != Profile::User {
        println!(
            "For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html"
        );
    }
}

// Used to get the path for `Subcommand::Setup`
pub fn interactive_path() -> io::Result<Profile> {
    fn abbrev_all() -> impl Iterator<Item = (String, Profile)> {
        ('a'..).map(|c| c.to_string()).zip(Profile::all())
    }

    fn parse_with_abbrev(input: &str) -> Result<Profile, String> {
        let input = input.trim().to_lowercase();
        for (letter, profile) in abbrev_all() {
            if input == letter {
                return Ok(profile);
            }
        }
        input.parse()
    }

    let mut input = String::new();
    println!("Welcome to the Rust project! What do you want to do with x.py?");
    for (letter, profile) in abbrev_all() {
        println!("{}) {}: {}", letter, profile, profile.purpose());
    }
    let template = loop {
        print!(
            "Please choose one ({}): ",
            abbrev_all().map(|(l, _)| l).collect::<Vec<_>>().join("/")
        );
        io::stdout().flush()?;
        io::stdin().read_line(&mut input)?;
        break match parse_with_abbrev(&input) {
            Ok(profile) => profile,
            Err(err) => {
                println!("error: {}", err);
                println!("note: press Ctrl+C to exit");
                continue;
            }
        };
    };
    Ok(template)
}

// install a git hook to automatically run tidy --bless, if they want
fn install_git_hook_maybe(src_path: &Path) -> io::Result<()> {
    let mut input = String::new();
    println!(
        "Rust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality.
If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` on each commit
to ensure your code is up to par. If you decide later that this behavior is undesirable,
simply delete the `pre-commit` file from .git/hooks."
    );

    let should_install = loop {
        print!("Would you like to install the git hook?: [y/N] ");
        io::stdout().flush()?;
        input.clear();
        io::stdin().read_line(&mut input)?;
        break match input.trim().to_lowercase().as_str() {
            "y" | "yes" => true,
            "n" | "no" | "" => false,
            _ => {
                println!("error: unrecognized option '{}'", input.trim());
                println!("note: press Ctrl+C to exit");
                continue;
            }
        };
    };

    Ok(if should_install {
        let src = src_path.join("src").join("etc").join("pre-commit.sh");
        let dst = src_path.join(".git").join("hooks").join("pre-commit");
        match fs::hard_link(src, dst) {
            Err(e) => println!(
                "x.py encountered an error -- do you already have the git hook installed?\n{}",
                e
            ),
            Ok(_) => println!("Linked `src/etc/pre-commit.sh` to `.git/hooks/pre-commit`"),
        };
    } else {
        println!("Ok, skipping installation!");
    })
}
<|endoftext|>


<commit_before>use num::{Integer, NumCast, Unsigned};
use std::hash::Hash;
use std::iter::FromIterator;

use generate::{HashIndexer, IndexVertices, IntoTriangles, IntoVertices, Topological, Triangulate};

pub struct ConjointBuffer<N, V>
where
    N: Integer + Unsigned,
{
    indeces: Vec<N>,
    vertices: Vec<V>,
}

impl<N, V> ConjointBuffer<N, V>
where
    N: Integer + Unsigned,
{
    pub fn new() -> Self {
        Self::default()
    }

    pub fn as_index_slice(&self) -> &[N] {
        self.indeces.as_slice()
    }

    pub fn as_vertex_slice(&self) -> &[V] {
        self.vertices.as_slice()
    }

    fn extend<I, J>(&mut self, indeces: I, vertices: J)
    where
        I: IntoIterator<Item = N>,
        J: IntoIterator<Item = V>,
    {
        self.indeces.extend(indeces);
        self.vertices.extend(vertices);
    }
}

impl<N, V> ConjointBuffer<N, V>
where
    N: Copy + Integer + NumCast + Unsigned,
{
    pub fn append<M, U>(&mut self, other: &mut ConjointBuffer<M, U>)
    where
        M: Copy + Integer + Into<N> + Unsigned,
        U: Into<V>,
    {
        let offset = N::from(self.vertices.len()).unwrap();
        self.vertices
            .extend(other.vertices.drain(..).map(|vertex| vertex.into()));
        self.indeces
            .extend(other.indeces.drain(..).map(|index| index.into() + offset))
    }
}

impl<N, V> Default for ConjointBuffer<N, V>
where
    N: Integer + Unsigned,
{
    fn default() -> Self {
        ConjointBuffer {
            indeces: vec![],
            vertices: vec![],
        }
    }
}

impl<T, N, V> FromIterator<T> for ConjointBuffer<N, V>
where
    T: IntoTriangles + IntoVertices + Topological,
    T::Vertex: Eq + Hash + Into<V>,
    N: Integer + NumCast + Unsigned,
{
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let (indeces, vertices) = input
            .into_iter()
            .triangulate()
            .index_vertices(HashIndexer::default());
        let mut buffer = ConjointBuffer::new();
        buffer.extend(
            indeces.into_iter().map(|index| N::from(index).unwrap()),
            vertices.into_iter().map(|vertex| vertex.into()),
        );
        buffer
    }
}

#[cfg(test)]
mod tests {
    use ordered_float::OrderedFloat;

    use buffer::conjoint::*;
    use generate::*;

    #[test]
    fn collect_topology_into_buffer() {
        type r32 = OrderedFloat<f32>;
        let buffer = sphere::UVSphere::<f32>::with_unit_radius(3, 2)
            .spatial_polygons() // 6 triangles, 18 vertices.
            .map_vertices(|(x, y, z)| (r32::from(x), r32::from(y), r32::from(z)))
            .triangulate()
            .collect::<ConjointBuffer<u64, (r32, r32, r32)>>();

        assert_eq!(18, buffer.as_index_slice().len());
        assert_eq!(5, buffer.as_vertex_slice().len());
    }
}
<commit_msg>Allow non-camel-case `r32` type name.<commit_after>use num::{Integer, NumCast, Unsigned};
use std::hash::Hash;
use std::iter::FromIterator;

use generate::{HashIndexer, IndexVertices, IntoTriangles, IntoVertices, Topological, Triangulate};

pub struct ConjointBuffer<N, V>
where
    N: Integer + Unsigned,
{
    indeces: Vec<N>,
    vertices: Vec<V>,
}

impl<N, V> ConjointBuffer<N, V>
where
    N: Integer + Unsigned,
{
    pub fn new() -> Self {
        Self::default()
    }

    pub fn as_index_slice(&self) -> &[N] {
        self.indeces.as_slice()
    }

    pub fn as_vertex_slice(&self) -> &[V] {
        self.vertices.as_slice()
    }

    fn extend<I, J>(&mut self, indeces: I, vertices: J)
    where
        I: IntoIterator<Item = N>,
        J: IntoIterator<Item = V>,
    {
        self.indeces.extend(indeces);
        self.vertices.extend(vertices);
    }
}

impl<N, V> ConjointBuffer<N, V>
where
    N: Copy + Integer + NumCast + Unsigned,
{
    pub fn append<M, U>(&mut self, other: &mut ConjointBuffer<M, U>)
    where
        M: Copy + Integer + Into<N> + Unsigned,
        U: Into<V>,
    {
        let offset = N::from(self.vertices.len()).unwrap();
        self.vertices
            .extend(other.vertices.drain(..).map(|vertex| vertex.into()));
        self.indeces
            .extend(other.indeces.drain(..).map(|index| index.into() + offset))
    }
}

impl<N, V> Default for ConjointBuffer<N, V>
where
    N: Integer + Unsigned,
{
    fn default() -> Self {
        ConjointBuffer {
            indeces: vec![],
            vertices: vec![],
        }
    }
}

impl<T, N, V> FromIterator<T> for ConjointBuffer<N, V>
where
    T: IntoTriangles + IntoVertices + Topological,
    T::Vertex: Eq + Hash + Into<V>,
    N: Integer + NumCast + Unsigned,
{
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let (indeces, vertices) = input
            .into_iter()
            .triangulate()
            .index_vertices(HashIndexer::default());
        let mut buffer = ConjointBuffer::new();
        buffer.extend(
            indeces.into_iter().map(|index| N::from(index).unwrap()),
            vertices.into_iter().map(|vertex| vertex.into()),
        );
        buffer
    }
}

#[cfg(test)]
mod tests {
    use ordered_float::OrderedFloat;

    use buffer::conjoint::*;
    use generate::*;

    #[test]
    fn collect_topology_into_buffer() {
        #[allow(non_camel_case_types)]
        type r32 = OrderedFloat<f32>;
        let buffer = sphere::UVSphere::<f32>::with_unit_radius(3, 2)
            .spatial_polygons() // 6 triangles, 18 vertices.
            .map_vertices(|(x, y, z)| (r32::from(x), r32::from(y), r32::from(z)))
            .triangulate()
            .collect::<ConjointBuffer<u64, (r32, r32, r32)>>();

        assert_eq!(18, buffer.as_index_slice().len());
        assert_eq!(5, buffer.as_vertex_slice().len());
    }
}
<|endoftext|>


<commit_before>// build-pass
//
// (this is deliberately *not* check-pass; I have confirmed that the bug in
// question does not replicate when one uses `cargo check` alone.)

pub enum Void {}

enum UninhabitedUnivariant { _Variant(Void), }

#[repr(C)]
enum UninhabitedUnivariantC { _Variant(Void), }

#[repr(i32)]
enum UninhabitedUnivariant32 { _Variant(Void), }

fn main() {
    let _seed: UninhabitedUnivariant = None.unwrap();
    match _seed {
        UninhabitedUnivariant::_Variant(_x) => {}
    }

    let _seed: UninhabitedUnivariantC = None.unwrap();
    match _seed {
        UninhabitedUnivariantC::_Variant(_x) => {}
    }

    let _seed: UninhabitedUnivariant32 = None.unwrap();
    match _seed {
        UninhabitedUnivariant32::_Variant(_x) => {}
    }
}
<commit_msg>Added oli's multivariant test case (alpha renaming the enum name itself).<commit_after>// build-pass
//
// (this is deliberately *not* check-pass; I have confirmed that the bug in
// question does not replicate when one uses `cargo check` alone.)

pub enum Void {}

enum UninhabitedUnivariant {
    _Variant(Void),
}

enum UninhabitedMultivariant2 {
    _Variant(Void),
    _Warriont(Void),
}

enum UninhabitedMultivariant3 {
    _Variant(Void),
    _Warriont(Void),
    _Worrynot(Void),
}

#[repr(C)]
enum UninhabitedUnivariantC {
    _Variant(Void),
}

#[repr(i32)]
enum UninhabitedUnivariant32 {
    _Variant(Void),
}

fn main() {
    let _seed: UninhabitedUnivariant = None.unwrap();
    match _seed {
        UninhabitedUnivariant::_Variant(_x) => {}
    }

    let _seed: UninhabitedMultivariant2 = None.unwrap();
    match _seed {
        UninhabitedMultivariant2::_Variant(_x) => {}
        UninhabitedMultivariant2::_Warriont(_x) => {}
    }

    let _seed: UninhabitedMultivariant2 = None.unwrap();
    match _seed {
        UninhabitedMultivariant2::_Variant(_x) => {}
        _ => {}
    }

    let _seed: UninhabitedMultivariant2 = None.unwrap();
    match _seed {
        UninhabitedMultivariant2::_Warriont(_x) => {}
        _ => {}
    }

    let _seed: UninhabitedMultivariant3 = None.unwrap();
    match _seed {
        UninhabitedMultivariant3::_Variant(_x) => {}
        UninhabitedMultivariant3::_Warriont(_x) => {}
        UninhabitedMultivariant3::_Worrynot(_x) => {}
    }

    let _seed: UninhabitedMultivariant3 = None.unwrap();
    match _seed {
        UninhabitedMultivariant3::_Variant(_x) => {}
        _ => {}
    }

    let _seed: UninhabitedMultivariant3 = None.unwrap();
    match _seed {
        UninhabitedMultivariant3::_Warriont(_x) => {}
        _ => {}
    }

    let _seed: UninhabitedMultivariant3 = None.unwrap();
    match _seed {
        UninhabitedMultivariant3::_Worrynot(_x) => {}
        _ => {}
    }

    let _seed: UninhabitedUnivariantC = None.unwrap();
    match _seed {
        UninhabitedUnivariantC::_Variant(_x) => {}
    }

    let _seed: UninhabitedUnivariant32 = None.unwrap();
    match _seed {
        UninhabitedUnivariant32::_Variant(_x) => {}
    }
}
<|endoftext|>


<commit_before>/// Module with functions and structs specific to brigde the las crate and laz crate to allow
/// writing & reading LAZ data

use std::io::{Write, Seek, Cursor, Read, SeekFrom};
use {Header, Point, Result, Vlr};
use writer::{PointWriter, write_point_to, write_header_and_vlrs_to};
use reader::{PointReader, read_point_from};
use laz::las::laszip::{LazVlr, LASZIP_DESCRIPTION, LASZIP_RECORD_ID, LASZIP_USER_ID};
use std::fmt::Debug;
use error::Error;

pub(crate) fn create_laszip_vlr(laszip_vlr: &LazVlr) -> std::io::Result<Vlr> {
    let mut cursor = Cursor::new(Vec::<u8>::new());
    laszip_vlr.write_to(&mut cursor)?;
    Ok(Vlr{
        user_id: LASZIP_USER_ID.to_owned(),
        record_id: LASZIP_RECORD_ID,
        description: LASZIP_DESCRIPTION.to_owned(),
        data: cursor.into_inner()
    })
}

pub(crate) fn extract_laszip_vlr(header: &mut Header) -> Option<Vlr> {
    let mut index = None;
    for (i, vlr) in header.vlrs().iter().enumerate() {
        if &vlr.user_id == LASZIP_USER_ID && vlr.record_id == LASZIP_RECORD_ID {
            index = Some(i);
        }
    }
    match index {
        Some(i) => Some(header.vlrs_mut().remove(i)),
        None => None
    }
}

/// struct that knows how to decompress LAZ
///
/// Decompression is done in 2 steps:
/// 1) call the decompressor that reads & decompress the next point
/// and put its data in an in-memory buffer
/// 2) read the buffer to get the decompress point
pub(crate) struct CompressedPointReader<R: Read +Seek> {
    /// decompressor that does the actual job
    decompressor: laz::las::laszip::LasZipDecompressor<R>,
    header: Header,
    /// in-memory buffer where the decompressor writes decompression result
    decompressor_output: Cursor<Vec<u8>>,
    last_point_idx: u64,
}

impl<R: Read + Seek> CompressedPointReader<R> {
    pub(crate) fn new(source: R, mut header: Header) -> Result<Self> {
        let laszip_vlr = match extract_laszip_vlr(&mut header) {
            None => return Err(Error::LasZipVlrNotFound),
            Some(vlr) => laz::las::laszip::LazVlr::from_buffer(&vlr.data)?
        };
        let decompressor_output = Cursor::new(vec![0u8; header.point_format().len() as usize]);

        Ok(Self {
            decompressor: laz::las::laszip::LasZipDecompressor::new(source, laszip_vlr)?,
            header,
            decompressor_output,
            last_point_idx: 0,
        })
    }
}

impl<R: Read + Seek> Debug for CompressedPointReader<R> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "CompressedPointReader(num_read: {}, header: {:?})", self.last_point_idx, self.header)
    }
}

impl<R: Read + Seek> PointReader for CompressedPointReader<R> {
    fn read_next(&mut self) -> Option<Result<Point>> {
        if self.last_point_idx < self.header.number_of_points() {
            self.last_point_idx += 1;
            self.decompressor.decompress_one(&mut self.decompressor_output.get_mut()).unwrap();
            if let Err(e) = self.decompressor_output.seek(SeekFrom::Start(0)) {
                Some(Err(e.into()))
            } else {
                Some(read_point_from(&mut self.decompressor_output, &self.header))
            }
        } else {

            None
        }
    }

    fn seek(&mut self, position: u64) -> Result<()> {
        self.last_point_idx = position - 1;
        self.decompressor.seek(position)?;
        Ok(())
    }

    fn header(&self) -> &Header {
        &self.header
    }
}

/// struct that knows how to write LAZ
///
/// Writing a point compressed is done in 2 steps
/// 1) write the point to a in-memory buffer
/// 2) call the laz compressor on this buffer
pub(crate) struct CompressedPointWriter<W: Write + Seek> {
    header: Header,
    /// buffer used to write the uncompressed point
    compressor_input: Cursor<Vec<u8>>,
    /// The compressor that actually does the job of compressing the data
    compressor: laz::las::laszip::LasZipCompressor<W>
}

impl<W: Write + Seek> CompressedPointWriter<W> {
    pub(crate) fn new(mut dest: W, mut header: Header) -> Result<Self> {
        if header.point_format().is_extended {
            panic!("Writing Extended point data is not supported");
        }

        let mut laz_items = laz::las::laszip::LazItemRecordBuilder::new();
        laz_items.add_item(laz::las::laszip::LazItemType::Point10);

        if header.point_format().has_gps_time {
            laz_items.add_item(laz::las::laszip::LazItemType::GpsTime);
        }

        if header.point_format().has_color {
            laz_items.add_item(laz::las::laszip::LazItemType::RGB12);
        }

        if header.point_format().extra_bytes > 0 {
            laz_items.add_item(laz::las::laszip::LazItemType::Byte(header.point_format().extra_bytes));
        }

        let laz_vlr = LazVlr::from_laz_items(laz_items.build());
        header.vlrs_mut().push(create_laszip_vlr(&laz_vlr)?);

        write_header_and_vlrs_to(&mut dest, &header)?;

        let compressor_input = Cursor::new(vec![0u8; header.point_format().len() as usize]);
        let compressor = laz::las::laszip::LasZipCompressor::from_laz_vlr(dest, laz_vlr)?;

        Ok(Self {
            header,
            compressor_input,
            compressor
        })
    }
}

impl<W: Write + Seek> PointWriter<W> for CompressedPointWriter<W> {
    fn write_next(&mut self, point: Point) -> Result<()> {
        self.header.add_point(&point);
        self.compressor_input.seek(SeekFrom::Start(0))?;
        write_point_to(&mut self.compressor_input, point, &self.header)?;
        self.compressor.compress_one(self.compressor_input.get_ref())?;
        Ok(())
    }

    fn into_inner(self: Box<Self>) -> W {
        self.compressor.into_stream()
    }

    fn get_mut(&mut self) -> &mut W {
        self.compressor.get_mut()
    }

    fn header(&self) -> &Header {
        &self.header
    }

    fn done(&mut self) -> Result<()> {
        self.compressor.done()?;
        Ok(())
    }
}

impl<W: Write + Seek> Debug for CompressedPointWriter<W> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "CompressedPointWriter(header: {:?})", self.header)
    }
}
<commit_msg>keep laszip vlr when creating reader, but clear all when creating writer<commit_after>/// Module with functions and structs specific to brigde the las crate and laz crate to allow
/// writing & reading LAZ data

use std::io::{Write, Seek, Cursor, Read, SeekFrom};
use {Header, Point, Result, Vlr};
use writer::{PointWriter, write_point_to, write_header_and_vlrs_to};
use reader::{PointReader, read_point_from};
use laz::las::laszip::{LazVlr, LASZIP_DESCRIPTION, LASZIP_RECORD_ID, LASZIP_USER_ID};
use std::fmt::Debug;
use error::Error;

fn is_laszip_vlr(vlr: &Vlr) -> bool {
    if &vlr.user_id == LASZIP_USER_ID && vlr.record_id == LASZIP_RECORD_ID {
        true
    } else {
        false
    }
}

fn create_laszip_vlr(laszip_vlr: &LazVlr) -> std::io::Result<Vlr> {
    let mut cursor = Cursor::new(Vec::<u8>::new());
    laszip_vlr.write_to(&mut cursor)?;
    Ok(Vlr{
        user_id: LASZIP_USER_ID.to_owned(),
        record_id: LASZIP_RECORD_ID,
        description: LASZIP_DESCRIPTION.to_owned(),
        data: cursor.into_inner()
    })
}

/// struct that knows how to decompress LAZ
///
/// Decompression is done in 2 steps:
/// 1) call the decompressor that reads & decompress the next point
/// and put its data in an in-memory buffer
/// 2) read the buffer to get the decompress point
pub(crate) struct CompressedPointReader<R: Read +Seek> {
    /// decompressor that does the actual job
    decompressor: laz::las::laszip::LasZipDecompressor<R>,
    header: Header,
    /// in-memory buffer where the decompressor writes decompression result
    decompressor_output: Cursor<Vec<u8>>,
    last_point_idx: u64,
}

impl<R: Read + Seek> CompressedPointReader<R> {
    pub(crate) fn new(source: R, header: Header) -> Result<Self> {
        let laszip_vlr = match header.vlrs().iter().find(|vlr| is_laszip_vlr(*vlr)) {
            None => return Err(Error::LasZipVlrNotFound),
            Some(ref vlr) => laz::las::laszip::LazVlr::from_buffer(&vlr.data)?
        };
        let decompressor_output = Cursor::new(vec![0u8; header.point_format().len() as usize]);

        Ok(Self {
            decompressor: laz::las::laszip::LasZipDecompressor::new(source, laszip_vlr)?,
            header,
            decompressor_output,
            last_point_idx: 0,
        })
    }
}

impl<R: Read + Seek> Debug for CompressedPointReader<R> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "CompressedPointReader(num_read: {}, header: {:?})", self.last_point_idx, self.header)
    }
}

impl<R: Read + Seek> PointReader for CompressedPointReader<R> {
    fn read_next(&mut self) -> Option<Result<Point>> {
        if self.last_point_idx < self.header.number_of_points() {
            self.last_point_idx += 1;
            self.decompressor.decompress_one(&mut self.decompressor_output.get_mut()).unwrap();
            if let Err(e) = self.decompressor_output.seek(SeekFrom::Start(0)) {
                Some(Err(e.into()))
            } else {
                Some(read_point_from(&mut self.decompressor_output, &self.header))
            }
        } else {
            None
        }
    }

    fn seek(&mut self, position: u64) -> Result<()> {
        self.last_point_idx = position - 1;
        self.decompressor.seek(position)?;
        Ok(())
    }

    fn header(&self) -> &Header {
        &self.header
    }
}

/// struct that knows how to write LAZ
///
/// Writing a point compressed is done in 2 steps
/// 1) write the point to a in-memory buffer
/// 2) call the laz compressor on this buffer
pub(crate) struct CompressedPointWriter<W: Write + Seek> {
    header: Header,
    /// buffer used to write the uncompressed point
    compressor_input: Cursor<Vec<u8>>,
    /// The compressor that actually does the job of compressing the data
    compressor: laz::las::laszip::LasZipCompressor<W>
}

impl<W: Write + Seek> CompressedPointWriter<W> {
    pub(crate) fn new(mut dest: W, mut header: Header) -> Result<Self> {
        if header.point_format().is_extended {
            panic!("Writing Extended point data is not supported");
        }

        let mut laz_items = laz::las::laszip::LazItemRecordBuilder::new();
        laz_items.add_item(laz::las::laszip::LazItemType::Point10);

        if header.point_format().has_gps_time {
            laz_items.add_item(laz::las::laszip::LazItemType::GpsTime);
        }

        if header.point_format().has_color {
            laz_items.add_item(laz::las::laszip::LazItemType::RGB12);
        }

        if header.point_format().extra_bytes > 0 {
            laz_items.add_item(laz::las::laszip::LazItemType::Byte(header.point_format().extra_bytes));
        }

        let laz_vlr = LazVlr::from_laz_items(laz_items.build());
        // Clear any existing laszip vlr as they might not be correct
        header.vlrs_mut().retain(|vlr| !is_laszip_vlr(vlr));
        header.vlrs_mut().push(create_laszip_vlr(&laz_vlr)?);

        write_header_and_vlrs_to(&mut dest, &header)?;

        let compressor_input = Cursor::new(vec![0u8; header.point_format().len() as usize]);
        let compressor = laz::las::laszip::LasZipCompressor::from_laz_vlr(dest, laz_vlr)?;

        Ok(Self {
            header,
            compressor_input,
            compressor
        })
    }
}

impl<W: Write + Seek> PointWriter<W> for CompressedPointWriter<W> {
    fn write_next(&mut self, point: Point) -> Result<()> {
        self.header.add_point(&point);
        self.compressor_input.seek(SeekFrom::Start(0))?;
        write_point_to(&mut self.compressor_input, point, &self.header)?;
        self.compressor.compress_one(self.compressor_input.get_ref())?;
        Ok(())
    }

    fn into_inner(self: Box<Self>) -> W {
        self.compressor.into_stream()
    }

    fn get_mut(&mut self) -> &mut W {
        self.compressor.get_mut()
    }

    fn header(&self) -> &Header {
        &self.header
    }

    fn done(&mut self) -> Result<()> {
        self.compressor.done()?;
        Ok(())
    }
}

impl<W: Write + Seek> Debug for CompressedPointWriter<W> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "CompressedPointWriter(header: {:?})", self.header)
    }
}
<|endoftext|>