<commit_before>//! Datagram Transport Layer Security Version 1.2 (RFC 6347)

use crate::tls::*;
use crate::TlsMessageAlert;
use nom::bytes::streaming::take;
use nom::combinator::{complete, cond, map, map_parser, verify};
use nom::error::{make_error, ErrorKind};
use nom::multi::{length_data, many1};
use nom::number::streaming::{be_u16, be_u24, be_u64, be_u8};

/// DTLS Plaintext record header
#[derive(Debug, PartialEq)]
pub struct DTLSRecordHeader {
    pub content_type: TlsRecordType,
    pub version: TlsVersion,
    /// A counter value that is incremented on every cipher state change.
    pub epoch: u16,
    /// The sequence number for this record.
    pub sequence_number: u64, // really an u48
    pub length: u16,
}

/// DTLS Plaintext record
///
/// Each DTLS record MUST fit within a single datagram.
///
/// Multiple DTLS records may be placed in a single datagram.
#[derive(Debug, PartialEq)]
pub struct DTLSPlaintext<'a> {
    pub header: DTLSRecordHeader,
    pub messages: Vec<DTLSMessage<'a>>,
}

#[derive(Debug, PartialEq)]
pub struct DTLSRawRecord<'a> {
    pub header: DTLSRecordHeader,
    pub fragment: &'a [u8],
}

#[derive(Debug, PartialEq)]
pub struct DTLSClientHello<'a> {
    pub version: TlsVersion,
    pub random: &'a [u8],
    pub session_id: Option<&'a [u8]>,
    pub cookie: &'a [u8],
    /// A list of ciphers supported by client
    pub ciphers: Vec<TlsCipherSuiteID>,
    /// A list of compression methods supported by client
    pub comp: Vec<TlsCompressionID>,
}

#[derive(Debug, PartialEq)]
pub struct DTLSHelloVerifyRequest<'a> {
    pub server_version: TlsVersion,
    pub cookie: &'a [u8],
}

/// DTLS Generic handshake message
#[derive(Debug, PartialEq)]
pub struct DTLSMessageHandshake<'a> {
    pub msg_type: TlsHandshakeType,
    pub length: u32,
    pub message_seq: u16,
    pub fragment_offset: u32,
    pub fragment_length: u32,
    pub body: DTLSMessageHandshakeBody<'a>,
}

/// DTLS Generic handshake message
#[derive(Debug, PartialEq)]
pub enum DTLSMessageHandshakeBody<'a> {
    HelloRequest,
    ClientHello(DTLSClientHello<'a>),
    HelloVerifyRequest(DTLSHelloVerifyRequest<'a>),
    ServerHello(TlsServerHelloContents<'a>),
    NewSessionTicket(TlsNewSessionTicketContent<'a>),
    HelloRetryRequest(TlsHelloRetryRequestContents<'a>),
    Certificate(TlsCertificateContents<'a>),
    ServerKeyExchange(TlsServerKeyExchangeContents<'a>),
    CertificateRequest(TlsCertificateRequestContents<'a>),
    ServerDone(&'a [u8]),
    CertificateVerify(&'a [u8]),
    ClientKeyExchange(TlsClientKeyExchangeContents<'a>),
    Finished(&'a [u8]),
    CertificateStatus(TlsCertificateStatusContents<'a>),
    NextProtocol(TlsNextProtocolContent<'a>),
}

/// DTLS plaintext message
///
/// Plaintext records can only be found during the handshake.
#[derive(Debug, PartialEq)]
pub enum DTLSMessage<'a> {
    Handshake(DTLSMessageHandshake<'a>),
    ChangeCipherSpec,
    Alert(TlsMessageAlert),
    ApplicationData(TlsMessageApplicationData<'a>),
    Heartbeat(TlsMessageHeartbeat<'a>),
}

// --------------------------- PARSERS ---------------------------

/// DTLS record header
// Section 4.1 of RFC6347
pub fn parse_dtls_record_header(i: &[u8]) -> IResult<&[u8], DTLSRecordHeader> {
    let (i, content_type) = TlsRecordType::parse(i)?;
    let (i, version) = TlsVersion::parse(i)?;
    let (i, int0) = be_u64(i)?;
    let epoch = (int0 >> 48) as u16;
    let sequence_number = int0 & 0xffff_ffff_ffff;
    let (i, length) = be_u16(i)?;
    let record = DTLSRecordHeader {
        content_type,
        version,
        epoch,
        sequence_number,
        length,
    };
    Ok((i, record))
}

/// DTLS Client Hello
// Section 4.2 of RFC6347
fn parse_dtls_client_hello(i: &[u8]) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    let (i, version) = TlsVersion::parse(i)?;
    let (i, random) = take(32usize)(i)?;
    let (i, sidlen) = verify(be_u8, |&n| n <= 32)(i)?;
    let (i, session_id) = cond(sidlen > 0, take(sidlen as usize))(i)?;
    let (i, cookie) = length_data(be_u8)(i)?;
    let (i, ciphers_len) = be_u16(i)?;
    let (i, ciphers) = parse_cipher_suites(i, ciphers_len as usize)?;
    let (i, comp_len) = be_u8(i)?;
    let (i, comp) = parse_compressions_algs(i, comp_len as usize)?;
    let content = DTLSClientHello {
        version,
        random,
        session_id,
        cookie,
        ciphers,
        comp,
    };
    Ok((i, DTLSMessageHandshakeBody::ClientHello(content)))
}

/// DTLS Client Hello
// Section 4.2 of RFC6347
fn parse_dtls_hello_verify_request(i: &[u8]) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    let (i, server_version) = TlsVersion::parse(i)?;
    let (i, cookie) = length_data(be_u8)(i)?;
    let content = DTLSHelloVerifyRequest {
        server_version,
        cookie,
    };
    Ok((i, DTLSMessageHandshakeBody::HelloVerifyRequest(content)))
}

fn parse_dtls_handshake_msg_server_hello_tlsv12(
    i: &[u8],
) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    map(
        parse_tls_server_hello_tlsv12,
        DTLSMessageHandshakeBody::ServerHello,
    )(i)
}

fn parse_dtls_handshake_msg_serverdone(
    i: &[u8],
    len: usize,
) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    map(take(len), DTLSMessageHandshakeBody::ServerDone)(i)
}

fn parse_dtls_handshake_msg_clientkeyexchange(
    i: &[u8],
    len: usize,
) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    map(
        parse_tls_clientkeyexchange(len),
        DTLSMessageHandshakeBody::ClientKeyExchange,
    )(i)
}

fn parse_dtls_handshake_msg_certificate(i: &[u8]) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    map(parse_tls_certificate, DTLSMessageHandshakeBody::Certificate)(i)
}

/// Parse a DTLS handshake message
pub fn parse_dtls_message_handshake(i: &[u8]) -> IResult<&[u8], DTLSMessage> {
    let (i, msg_type) = map(be_u8, TlsHandshakeType)(i)?;
    let (i, length) = be_u24(i)?;
    let (i, message_seq) = be_u16(i)?;
    let (i, fragment_offset) = be_u24(i)?;
    let (i, fragment_length) = be_u24(i)?;
    let (i, raw_msg) = take(length)(i)?;
    let (_, body) = match msg_type {
        TlsHandshakeType::ClientHello => parse_dtls_client_hello(raw_msg),
        TlsHandshakeType::HelloVerifyRequest => parse_dtls_hello_verify_request(raw_msg),
        TlsHandshakeType::ServerHello => parse_dtls_handshake_msg_server_hello_tlsv12(raw_msg),
        TlsHandshakeType::ServerDone => {
            parse_dtls_handshake_msg_serverdone(raw_msg, length as usize)
        }
        TlsHandshakeType::ClientKeyExchange => {
            parse_dtls_handshake_msg_clientkeyexchange(raw_msg, length as usize)
        }
        TlsHandshakeType::Certificate => parse_dtls_handshake_msg_certificate(raw_msg),
        _ => {
            // eprintln!("Unsupported message type {:?}", msg_type);
            Err(Err::Error(make_error(i, ErrorKind::Switch)))
        }
    }?;
    let msg = DTLSMessageHandshake {
        msg_type,
        length,
        message_seq,
        fragment_offset,
        fragment_length,
        body,
    };
    Ok((i, DTLSMessage::Handshake(msg)))
}

/// Parse a DTLS changecipherspec message
// XXX add extra verification hdr.len == 1
pub fn parse_dtls_message_changecipherspec(i: &[u8]) -> IResult<&[u8], DTLSMessage> {
    let (i, _) = verify(be_u8, |&tag| tag == 0x01)(i)?;
    Ok((i, DTLSMessage::ChangeCipherSpec))
}

/// Parse a DTLS alert message
// XXX add extra verification hdr.len == 2
pub fn parse_dtls_message_alert(i: &[u8]) -> IResult<&[u8], DTLSMessage> {
    let (i, alert) = TlsMessageAlert::parse(i)?;
    Ok((i, DTLSMessage::Alert(alert)))
}

pub fn parse_dtls_record_with_header<'i, 'hdr>(
    i: &'i [u8],
    hdr: &'hdr DTLSRecordHeader,
) -> IResult<&'i [u8], Vec<DTLSMessage<'i>>> {
    match hdr.content_type {
        TlsRecordType::ChangeCipherSpec => many1(complete(parse_dtls_message_changecipherspec))(i),
        TlsRecordType::Alert => many1(complete(parse_dtls_message_alert))(i),
        TlsRecordType::Handshake => many1(complete(parse_dtls_message_handshake))(i),
        // TlsRecordType::ApplicationData  => many1(complete(parse_tls_message_applicationdata))(i),
        // TlsRecordType::Heartbeat        => parse_tls_message_heartbeat(i, hdr.length),
        _ => {
            // eprintln!("Unsupported record type {:?}", hdr.content_type);
            Err(Err::Error(make_error(i, ErrorKind::Switch)))
        }
    }
}

/// Parse DTLS record, leaving `fragment` unparsed
// Section 4.1 of RFC6347
pub fn parse_dtls_raw_record(i: &[u8]) -> IResult<&[u8], DTLSRawRecord> {
    let (i, header) = parse_dtls_record_header(i)?;
    // As in TLS 1.2, the length should not exceed 2^14.
    if header.length > MAX_RECORD_LEN {
        return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
    }
    let (i, fragment) = take(header.length as usize)(i)?;
    Ok((i, DTLSRawRecord { header, fragment }))
}

/// Parse one DTLS plaintext record
// Section 4.1 of RFC6347
pub fn parse_dtls_plaintext_record(i: &[u8]) -> IResult<&[u8], DTLSPlaintext> {
    let (i, header) = parse_dtls_record_header(i)?;
    // As in TLS 1.2, the length should not exceed 2^14.
    if header.length > MAX_RECORD_LEN {
        return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
    }
    let (i, messages) = map_parser(take(header.length as usize), |i| {
        parse_dtls_record_with_header(i, &header)
    })(i)?;
    Ok((i, DTLSPlaintext { header, messages }))
}

/// Parse multiple DTLS plaintext record
// Section 4.1 of RFC6347
pub fn parse_dtls_plaintext_records(i: &[u8]) -> IResult<&[u8], Vec<DTLSPlaintext>> {
    many1(complete(parse_dtls_plaintext_record))(i)
}
<commit_msg>dtls support get extension buffer<commit_after>//! Datagram Transport Layer Security Version 1.2 (RFC 6347)

use crate::tls::*;
use crate::TlsMessageAlert;
use nom::bytes::streaming::take;
use nom::combinator::{complete, cond, map, map_parser, opt, verify};
use nom::error::{make_error, ErrorKind};
use nom::multi::{length_data, many1};
use nom::number::streaming::{be_u16, be_u24, be_u64, be_u8};

/// DTLS Plaintext record header
#[derive(Debug, PartialEq)]
pub struct DTLSRecordHeader {
    pub content_type: TlsRecordType,
    pub version: TlsVersion,
    /// A counter value that is incremented on every cipher state change.
    pub epoch: u16,
    /// The sequence number for this record.
    pub sequence_number: u64, // really an u48
    pub length: u16,
}

/// DTLS Plaintext record
///
/// Each DTLS record MUST fit within a single datagram.
///
/// Multiple DTLS records may be placed in a single datagram.
#[derive(Debug, PartialEq)]
pub struct DTLSPlaintext<'a> {
    pub header: DTLSRecordHeader,
    pub messages: Vec<DTLSMessage<'a>>,
}

#[derive(Debug, PartialEq)]
pub struct DTLSRawRecord<'a> {
    pub header: DTLSRecordHeader,
    pub fragment: &'a [u8],
}

#[derive(Debug, PartialEq)]
pub struct DTLSClientHello<'a> {
    pub version: TlsVersion,
    pub random: &'a [u8],
    pub session_id: Option<&'a [u8]>,
    pub cookie: &'a [u8],
    /// A list of ciphers supported by client
    pub ciphers: Vec<TlsCipherSuiteID>,
    /// A list of compression methods supported by client
    pub comp: Vec<TlsCompressionID>,
    pub ext: Option<&'a [u8]>,
}

#[derive(Debug, PartialEq)]
pub struct DTLSHelloVerifyRequest<'a> {
    pub server_version: TlsVersion,
    pub cookie: &'a [u8],
}

/// DTLS Generic handshake message
#[derive(Debug, PartialEq)]
pub struct DTLSMessageHandshake<'a> {
    pub msg_type: TlsHandshakeType,
    pub length: u32,
    pub message_seq: u16,
    pub fragment_offset: u32,
    pub fragment_length: u32,
    pub body: DTLSMessageHandshakeBody<'a>,
}

/// DTLS Generic handshake message
#[derive(Debug, PartialEq)]
pub enum DTLSMessageHandshakeBody<'a> {
    HelloRequest,
    ClientHello(DTLSClientHello<'a>),
    HelloVerifyRequest(DTLSHelloVerifyRequest<'a>),
    ServerHello(TlsServerHelloContents<'a>),
    NewSessionTicket(TlsNewSessionTicketContent<'a>),
    HelloRetryRequest(TlsHelloRetryRequestContents<'a>),
    Certificate(TlsCertificateContents<'a>),
    ServerKeyExchange(TlsServerKeyExchangeContents<'a>),
    CertificateRequest(TlsCertificateRequestContents<'a>),
    ServerDone(&'a [u8]),
    CertificateVerify(&'a [u8]),
    ClientKeyExchange(TlsClientKeyExchangeContents<'a>),
    Finished(&'a [u8]),
    CertificateStatus(TlsCertificateStatusContents<'a>),
    NextProtocol(TlsNextProtocolContent<'a>),
}

/// DTLS plaintext message
///
/// Plaintext records can only be found during the handshake.
#[derive(Debug, PartialEq)]
pub enum DTLSMessage<'a> {
    Handshake(DTLSMessageHandshake<'a>),
    ChangeCipherSpec,
    Alert(TlsMessageAlert),
    ApplicationData(TlsMessageApplicationData<'a>),
    Heartbeat(TlsMessageHeartbeat<'a>),
}

// --------------------------- PARSERS ---------------------------

/// DTLS record header
// Section 4.1 of RFC6347
pub fn parse_dtls_record_header(i: &[u8]) -> IResult<&[u8], DTLSRecordHeader> {
    let (i, content_type) = TlsRecordType::parse(i)?;
    let (i, version) = TlsVersion::parse(i)?;
    let (i, int0) = be_u64(i)?;
    let epoch = (int0 >> 48) as u16;
    let sequence_number = int0 & 0xffff_ffff_ffff;
    let (i, length) = be_u16(i)?;
    let record = DTLSRecordHeader {
        content_type,
        version,
        epoch,
        sequence_number,
        length,
    };
    Ok((i, record))
}

/// DTLS Client Hello
// Section 4.2 of RFC6347
fn parse_dtls_client_hello(i: &[u8]) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    let (i, version) = TlsVersion::parse(i)?;
    let (i, random) = take(32usize)(i)?;
    let (i, sidlen) = verify(be_u8, |&n| n <= 32)(i)?;
    let (i, session_id) = cond(sidlen > 0, take(sidlen as usize))(i)?;
    let (i, cookie) = length_data(be_u8)(i)?;
    let (i, ciphers_len) = be_u16(i)?;
    let (i, ciphers) = parse_cipher_suites(i, ciphers_len as usize)?;
    let (i, comp_len) = be_u8(i)?;
    let (i, comp) = parse_compressions_algs(i, comp_len as usize)?;
    let (i, ext) = opt(complete(length_data(be_u16)))(i)?;
    let content = DTLSClientHello {
        version,
        random,
        session_id,
        cookie,
        ciphers,
        comp,
        ext,
    };
    Ok((i, DTLSMessageHandshakeBody::ClientHello(content)))
}

/// DTLS Client Hello
// Section 4.2 of RFC6347
fn parse_dtls_hello_verify_request(i: &[u8]) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    let (i, server_version) = TlsVersion::parse(i)?;
    let (i, cookie) = length_data(be_u8)(i)?;
    let content = DTLSHelloVerifyRequest {
        server_version,
        cookie,
    };
    Ok((i, DTLSMessageHandshakeBody::HelloVerifyRequest(content)))
}

fn parse_dtls_handshake_msg_server_hello_tlsv12(
    i: &[u8],
) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    map(
        parse_tls_server_hello_tlsv12,
        DTLSMessageHandshakeBody::ServerHello,
    )(i)
}

fn parse_dtls_handshake_msg_serverdone(
    i: &[u8],
    len: usize,
) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    map(take(len), DTLSMessageHandshakeBody::ServerDone)(i)
}

fn parse_dtls_handshake_msg_clientkeyexchange(
    i: &[u8],
    len: usize,
) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    map(
        parse_tls_clientkeyexchange(len),
        DTLSMessageHandshakeBody::ClientKeyExchange,
    )(i)
}

fn parse_dtls_handshake_msg_certificate(i: &[u8]) -> IResult<&[u8], DTLSMessageHandshakeBody> {
    map(parse_tls_certificate, DTLSMessageHandshakeBody::Certificate)(i)
}

/// Parse a DTLS handshake message
pub fn parse_dtls_message_handshake(i: &[u8]) -> IResult<&[u8], DTLSMessage> {
    let (i, msg_type) = map(be_u8, TlsHandshakeType)(i)?;
    let (i, length) = be_u24(i)?;
    let (i, message_seq) = be_u16(i)?;
    let (i, fragment_offset) = be_u24(i)?;
    let (i, fragment_length) = be_u24(i)?;
    let (i, raw_msg) = take(length)(i)?;
    let (_, body) = match msg_type {
        TlsHandshakeType::ClientHello => parse_dtls_client_hello(raw_msg),
        TlsHandshakeType::HelloVerifyRequest => parse_dtls_hello_verify_request(raw_msg),
        TlsHandshakeType::ServerHello => parse_dtls_handshake_msg_server_hello_tlsv12(raw_msg),
        TlsHandshakeType::ServerDone => {
            parse_dtls_handshake_msg_serverdone(raw_msg, length as usize)
        }
        TlsHandshakeType::ClientKeyExchange => {
            parse_dtls_handshake_msg_clientkeyexchange(raw_msg, length as usize)
        }
        TlsHandshakeType::Certificate => parse_dtls_handshake_msg_certificate(raw_msg),
        _ => {
            // eprintln!("Unsupported message type {:?}", msg_type);
            Err(Err::Error(make_error(i, ErrorKind::Switch)))
        }
    }?;
    let msg = DTLSMessageHandshake {
        msg_type,
        length,
        message_seq,
        fragment_offset,
        fragment_length,
        body,
    };
    Ok((i, DTLSMessage::Handshake(msg)))
}

/// Parse a DTLS changecipherspec message
// XXX add extra verification hdr.len == 1
pub fn parse_dtls_message_changecipherspec(i: &[u8]) -> IResult<&[u8], DTLSMessage> {
    let (i, _) = verify(be_u8, |&tag| tag == 0x01)(i)?;
    Ok((i, DTLSMessage::ChangeCipherSpec))
}

/// Parse a DTLS alert message
// XXX add extra verification hdr.len == 2
pub fn parse_dtls_message_alert(i: &[u8]) -> IResult<&[u8], DTLSMessage> {
    let (i, alert) = TlsMessageAlert::parse(i)?;
    Ok((i, DTLSMessage::Alert(alert)))
}

pub fn parse_dtls_record_with_header<'i, 'hdr>(
    i: &'i [u8],
    hdr: &'hdr DTLSRecordHeader,
) -> IResult<&'i [u8], Vec<DTLSMessage<'i>>> {
    match hdr.content_type {
        TlsRecordType::ChangeCipherSpec => many1(complete(parse_dtls_message_changecipherspec))(i),
        TlsRecordType::Alert => many1(complete(parse_dtls_message_alert))(i),
        TlsRecordType::Handshake => many1(complete(parse_dtls_message_handshake))(i),
        // TlsRecordType::ApplicationData  => many1(complete(parse_tls_message_applicationdata))(i),
        // TlsRecordType::Heartbeat        => parse_tls_message_heartbeat(i, hdr.length),
        _ => {
            // eprintln!("Unsupported record type {:?}", hdr.content_type);
            Err(Err::Error(make_error(i, ErrorKind::Switch)))
        }
    }
}

/// Parse DTLS record, leaving `fragment` unparsed
// Section 4.1 of RFC6347
pub fn parse_dtls_raw_record(i: &[u8]) -> IResult<&[u8], DTLSRawRecord> {
    let (i, header) = parse_dtls_record_header(i)?;
    // As in TLS 1.2, the length should not exceed 2^14.
    if header.length > MAX_RECORD_LEN {
        return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
    }
    let (i, fragment) = take(header.length as usize)(i)?;
    Ok((i, DTLSRawRecord { header, fragment }))
}

/// Parse one DTLS plaintext record
// Section 4.1 of RFC6347
pub fn parse_dtls_plaintext_record(i: &[u8]) -> IResult<&[u8], DTLSPlaintext> {
    let (i, header) = parse_dtls_record_header(i)?;
    // As in TLS 1.2, the length should not exceed 2^14.
    if header.length > MAX_RECORD_LEN {
        return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
    }
    let (i, messages) = map_parser(take(header.length as usize), |i| {
        parse_dtls_record_with_header(i, &header)
    })(i)?;
    Ok((i, DTLSPlaintext { header, messages }))
}

/// Parse multiple DTLS plaintext record
// Section 4.1 of RFC6347
pub fn parse_dtls_plaintext_records(i: &[u8]) -> IResult<&[u8], Vec<DTLSPlaintext>> {
    many1(complete(parse_dtls_plaintext_record))(i)
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use parse::Env;
use var::Var;
use source::Src;

pub trait Eval {
    fn eval (&self, lookup: &str) -> Option<Var>;
}

pub struct Evaluator<'e, 'd, D:Eval + 'd> {
    data: &'d D,
    env: &'e mut Env,
    pub next_node: String,
}

impl<'e, 'd, D:Eval + 'd> Iterator for Evaluator<'e, 'd, D>
    where D: Eval + 'd {

    type Item = (Vec<Var>,Option<String>);
    fn next(&mut self) -> Option<Self::Item> {
        if self.next_node.is_empty() { return None }

        let nn = self.next_node.clone();
        let r = self.run(&nn);
        if let Some(nn) = r.1.clone() {
            self.next_node = nn;
        }
        else { self.next_node = "".to_owned(); }

        Some(r)
    }
}

impl<'e, 'd, D:Eval> Evaluator<'e, 'd, D> {
    pub fn new (env: &'e mut Env, data: &'d D) -> Evaluator<'e, 'd, D> {
        Evaluator { env: env, data: data, next_node: "root".to_owned() }
    }

    pub fn run (&mut self, node_name: &str)
                -> (Vec<Var>,Option<String>)
        where D: Eval + 'd
    {
        let mut r = vec!();
        let mut node = None;

        if let Some(b) = self.env.src.get_mut(node_name) {
            let mut state: HashMap<String,bool> = HashMap::new();

            let yield_idx = b.await_idx;
            b.await_idx = 0;

            for (i,src) in b.src[yield_idx..].iter().enumerate() {
                match src {
                    &Src::Await(ref nn) => {
                        b.await_idx = i+1;
                        node = nn.clone();
                        break
                    },
                    _ => {}
                }

                let (mut vars, node_) = src.eval(&mut state, self.data);
                for n in vars.drain(..) { r.push(n); }
                if node_.is_some() { node = node_; break; }
            }
        }

        for var in r.iter_mut() {
            let mut val = None;
            match var {
                &mut Var::String(ref mut s) => { //format string
                    let mut fs = String::new();
                    let mut started = false;

                    // NOTE: we should move this out to a SYM varkind instead
                    // (parsed earlier)
                    if s.split_terminator(' ').count() == 1 {
                        if s.chars().next().unwrap() == '`' {
                            if let Some(ref val_) = self.data.eval(&s[1..]) {
                                val = Some(val_.clone());
                            }
                        }
                    }
                    else {
                        for word in s.split_terminator(' ') {
                            if started { fs.push(' '); }

                            if word.chars().next().unwrap() == '`' {
                                if let Some(ref val_) = self.data.eval(&word[1..]) {
                                    fs.push_str(&val_.to_string());
                                }
                            }
                            else {
                                fs.push_str(word);
                            }

                            started = true;
                        }
                        *s = fs;
                    }
                },
                _ => {}
            }

            if let Some(val) = val {
                *var = val;
            }
        }

        return (r,node)
    }
}
<commit_msg>await feature<commit_after>use std::collections::HashMap;
use parse::Env;
use var::Var;
use source::Src;

pub trait Eval {
    fn eval (&self, lookup: &str) -> Option<Var>;
}

pub struct Evaluator<'e, 'd, D:Eval + 'd> {
    data: &'d D,
    env: &'e mut Env,
    pub next_node: String,
}

impl<'e, 'd, D:Eval + 'd> Iterator for Evaluator<'e, 'd, D>
    where D: Eval + 'd {

    type Item = (Vec<Var>,Option<String>);
    fn next(&mut self) -> Option<Self::Item> {
        if self.next_node.is_empty() { return None }

        let nn = self.next_node.clone();
        let r = self.run(&nn);
        if let Some(nn) = r.1.clone() {
            self.next_node = nn;
        }
        else { self.next_node = "".to_owned(); }

        Some(r)
    }
}

impl<'e, 'd, D:Eval> Evaluator<'e, 'd, D> {
    pub fn new (env: &'e mut Env, data: &'d D) -> Evaluator<'e, 'd, D> {
        Evaluator { env: env, data: data, next_node: "root".to_owned() }
    }

    pub fn run (&mut self, node_name: &str)
                -> (Vec<Var>,Option<String>)
        where D: Eval + 'd
    {
        let mut r = vec!();
        let mut node = None;

        if let Some(b) = self.env.src.get_mut(node_name) {
            let mut state: HashMap<String,bool> = HashMap::new();

            let await_idx = b.await_idx;
            b.await_idx = 0;

            for (i,src) in b.src[await_idx..].iter().enumerate() {
                match src {
                    &Src::Await(ref nn) => {
                        b.await_idx = i+1;
                        node = nn.clone();
                        break
                    },
                    _ => {}
                }

                let (mut vars, node_) = src.eval(&mut state, self.data);
                for n in vars.drain(..) { r.push(n); }
                if node_.is_some() { node = node_; break; }
            }
        }

        for var in r.iter_mut() {
            let mut val = None;
            match var {
                &mut Var::String(ref mut s) => { //format string
                    let mut fs = String::new();
                    let mut started = false;

                    // NOTE: we should move this out to a SYM varkind instead
                    // (parsed earlier)
                    if s.split_terminator(' ').count() == 1 {
                        if s.chars().next().unwrap() == '`' {
                            if let Some(ref val_) = self.data.eval(&s[1..]) {
                                val = Some(val_.clone());
                            }
                        }
                    }
                    else {
                        for word in s.split_terminator(' ') {
                            if started { fs.push(' '); }

                            if word.chars().next().unwrap() == '`' {
                                if let Some(ref val_) = self.data.eval(&word[1..]) {
                                    fs.push_str(&val_.to_string());
                                }
                            }
                            else {
                                fs.push_str(word);
                            }

                            started = true;
                        }
                        *s = fs;
                    }
                },
                _ => {}
            }

            if let Some(val) = val {
                *var = val;
            }
        }

        return (r,node)
    }
}
<|endoftext|>


<commit_before>use ffi::types::{hid_t, hsize_t, hbool_t};
use ffi::h5i::H5I_INVALID_HID;
use ffi::h5p::{H5P_FILE_CREATE, H5P_FILE_ACCESS, H5Pcreate, H5Pset_userblock};
use ffi::h5f::{H5F_ACC_RDONLY, H5F_ACC_RDWR, H5F_ACC_EXCL, H5F_ACC_TRUNC,
               H5Fopen, H5Fcreate, H5Fclose, H5Fget_filesize, H5Fget_intent,
               H5Fget_access_plist};
use ffi::drivers::{H5Pset_fapl_sec2, H5Pset_fapl_stdio, H5Pset_fapl_core};

use object::{Handle, Object};
use error::Result;
use plist::PropertyList;
use ffi::util::string_to_cstr;
use std::path::{Path, PathBuf};

use libc::{size_t, c_uint};

#[derive(Clone)]
pub struct File {
    handle: Handle,
}

impl Object for File {
    fn id(&self) -> hid_t {
        self.handle.id()
    }

    fn from_id(id: hid_t) -> File {
        File { handle: Handle::new(id) }
    }
}

impl File {
    pub fn size(&self) -> u64 {
        unsafe {
            let size: *mut hsize_t = &mut 0;
            h5lock_s!(H5Fget_filesize(self.id(), size));
            if *size > 0 { *size as u64 } else { 0 }
        }
    }

    pub fn is_read_only(&self) -> bool {
        unsafe {
            let mode: *mut c_uint = &mut 0;
            h5lock_s!(H5Fget_intent(self.id(), mode));
            *mode == H5F_ACC_RDONLY
        }
    }

    pub fn open<P: AsRef<Path>, S: Into<String>>(filename: P, mode: S) -> Result<File> {
        FileBuilder::new(filename).mode(mode).open()
    }

    fn fapl(&self) -> PropertyList {
        PropertyList::from_id(h5call!(H5Fget_access_plist(self.id())).unwrap_or(H5I_INVALID_HID))
    }
}

impl Drop for File {
    fn drop(&mut self) {
        if self.refcount() == 1 {
            h5lock!(H5Fclose(self.id()));
        }
    }
}

pub struct FileBuilder {
    filename: PathBuf,
    driver: String,
    mode: String,
    userblock: size_t,
    filebacked: bool,
    increment: size_t,
}

impl FileBuilder {
    pub fn new<P: AsRef<Path>>(filename: P) -> FileBuilder {
        FileBuilder {
            filename: filename.as_ref().to_path_buf(),
            driver: "sec2".to_string(),
            mode: "r".to_string(),
            userblock: 0,
            filebacked: false,
            increment: 64 * 1024 * 1024,
        }
    }

    pub fn driver<S: Into<String>>(&mut self, driver: S) -> &mut FileBuilder {
        self.driver = driver.into(); self
    }

    pub fn mode<S: Into<String>>(&mut self, mode: S) -> &mut FileBuilder {
        self.mode = mode.into(); self
    }

    pub fn userblock(&mut self, userblock: size_t) -> &mut FileBuilder {
        self.userblock = userblock; self
    }

    pub fn filebacked(&mut self, filebacked: bool) -> &mut FileBuilder {
        self.filebacked = filebacked; self
    }

    pub fn increment(&mut self, increment: size_t) -> &mut FileBuilder {
        self.increment = increment; self
    }

    fn make_fapl(&self) -> Result<PropertyList> {
        h5lock_s!({
            let fapl = PropertyList::from_id(h5try!(H5Pcreate(*H5P_FILE_ACCESS)));
            match self.driver.as_ref() {
                "sec2"  => h5try!(H5Pset_fapl_sec2(fapl.id())),
                "stdio" => h5try!(H5Pset_fapl_stdio(fapl.id())),
                "core"  => h5try!(H5Pset_fapl_core(fapl.id(), self.increment,
                                                   self.filebacked as hbool_t)),
                _       => fail!(format!("Invalid file driver: {}", self.driver)),
            };
            Ok(fapl)
        })
    }

    fn open_file(&self, write: bool) -> Result<File> {
        ensure!(self.userblock == 0, "Cannot specify userblock when opening a file");
        h5lock_s!({
            let fapl = try!(self.make_fapl());
            let flags = if write { H5F_ACC_RDWR } else { H5F_ACC_RDONLY };
            match self.filename.to_str() {
                Some(filename) => {
                    let c_filename = string_to_cstr(filename);
                    let file = File::from_id(h5try!(H5Fopen(c_filename, flags, fapl.id())));
                    ensure!(file.is_valid(), "Invalid id for opened file");
                    Ok(file)
                },
                None          => fail!("Invalid UTF-8 in file name: {:?}", self.filename)
            }
        })
    }

    fn create_file(&self, exclusive: bool) -> Result<File> {
        h5lock_s!({
            let fcpl = PropertyList::from_id(h5try!(H5Pcreate(*H5P_FILE_CREATE)));
            h5try!(H5Pset_userblock(fcpl.id(), self.userblock));
            let fapl = try!(self.make_fapl());
            let flags = if exclusive { H5F_ACC_EXCL } else { H5F_ACC_TRUNC };
            match self.filename.to_str() {
                Some(filename) => {
                    let c_filename = string_to_cstr(filename);
                    let file = File::from_id(h5try!(H5Fcreate(c_filename, flags,
                                                              fcpl.id(), fapl.id())));
                    ensure!(file.is_valid(), "Invalid id for created file");
                    Ok(file)
                },
                None          => fail!("Invalid UTF-8 in file name: {:?}", self.filename)
            }
        })
    }

    pub fn open(&self) -> Result<File> {
        match self.mode.as_ref() {
            "r"        => self.open_file(false),
            "r+"       => self.open_file(true),
            "w"        => self.create_file(false),
            "w-" | "x" => self.create_file(true),
            "a"        => match self.open_file(true) {
                            Ok(file) => Ok(file),
                            _        => self.create_file(true),
                          },
            _          => fail!("Invalid file access mode, expected r|r+|w|w-|x|a"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::File;
    use std::path::PathBuf;
    use tempdir::TempDir;

    fn with_tmpdir<F: Fn(PathBuf)>(f: F) {
        let dir = TempDir::new_in(".", "tmp").unwrap();
        let path = dir.path().to_path_buf();
        f(path);
    }

    #[test]
    pub fn test_invalid_mode() {
        with_tmpdir(|dir| {
            assert_err!(File::open(&dir, "foo"), "Invalid file access mode");
        })
    }

    #[test]
    pub fn test_is_read_only() {
        with_tmpdir(|dir| {
            let path = dir.join("foo.h5");
            assert!(!File::open(&path, "w").unwrap().is_read_only());
            assert!(File::open(&path, "r").unwrap().is_read_only());
            assert!(!File::open(&path, "r+").unwrap().is_read_only());
            assert!(!File::open(&path, "a").unwrap().is_read_only());
        });
        with_tmpdir(|dir| {
            assert!(!File::open(dir.join("foo.h"), "a").unwrap().is_read_only());
        });
        with_tmpdir(|dir| {
            assert!(!File::open(dir.join("foo.h"), "x").unwrap().is_read_only());
        });
    }

    #[test]
    pub fn test_invalid_filename() {
        with_tmpdir(|dir| {
            assert_err!(File::open(&dir, "r"), "unable to open file");
            assert_err!(File::open(&dir, "r+"), "unable to open file");
            assert_err!(File::open(&dir, "x"), "unable to create file");
            assert_err!(File::open(&dir, "w-"), "unable to create file");
            assert_err!(File::open(&dir, "w"), "unable to create file");
            assert_err!(File::open(&dir, "a"), "unable to create file");
        });
    }
}
<commit_msg>Add File::fcpl()<commit_after>use ffi::types::{hid_t, hsize_t, hbool_t};
use ffi::h5i::H5I_INVALID_HID;
use ffi::h5p::{H5P_FILE_CREATE, H5P_FILE_ACCESS, H5Pcreate, H5Pset_userblock};
use ffi::h5f::{H5F_ACC_RDONLY, H5F_ACC_RDWR, H5F_ACC_EXCL, H5F_ACC_TRUNC,
               H5Fopen, H5Fcreate, H5Fclose, H5Fget_filesize, H5Fget_intent,
               H5Fget_access_plist, H5Fget_create_plist};
use ffi::drivers::{H5Pset_fapl_sec2, H5Pset_fapl_stdio, H5Pset_fapl_core};

use object::{Handle, Object};
use error::Result;
use plist::PropertyList;
use ffi::util::string_to_cstr;
use std::path::{Path, PathBuf};

use libc::{size_t, c_uint};

#[derive(Clone)]
pub struct File {
    handle: Handle,
}

impl Object for File {
    fn id(&self) -> hid_t {
        self.handle.id()
    }

    fn from_id(id: hid_t) -> File {
        File { handle: Handle::new(id) }
    }
}

impl File {
    pub fn size(&self) -> u64 {
        unsafe {
            let size: *mut hsize_t = &mut 0;
            h5lock_s!(H5Fget_filesize(self.id(), size));
            if *size > 0 { *size as u64 } else { 0 }
        }
    }

    pub fn is_read_only(&self) -> bool {
        unsafe {
            let mode: *mut c_uint = &mut 0;
            h5lock_s!(H5Fget_intent(self.id(), mode));
            *mode == H5F_ACC_RDONLY
        }
    }

    pub fn open<P: AsRef<Path>, S: Into<String>>(filename: P, mode: S) -> Result<File> {
        FileBuilder::new(filename).mode(mode).open()
    }

    fn fapl(&self) -> PropertyList {
        PropertyList::from_id(h5call!(H5Fget_access_plist(self.id())).unwrap_or(H5I_INVALID_HID))
    }

    fn fcpl(&self) -> PropertyList {
        PropertyList::from_id(h5call!(H5Fget_create_plist(self.id())).unwrap_or(H5I_INVALID_HID))
    }
}

impl Drop for File {
    fn drop(&mut self) {
        if self.refcount() == 1 {
            h5lock!(H5Fclose(self.id()));
        }
    }
}

pub struct FileBuilder {
    filename: PathBuf,
    driver: String,
    mode: String,
    userblock: size_t,
    filebacked: bool,
    increment: size_t,
}

impl FileBuilder {
    pub fn new<P: AsRef<Path>>(filename: P) -> FileBuilder {
        FileBuilder {
            filename: filename.as_ref().to_path_buf(),
            driver: "sec2".to_string(),
            mode: "r".to_string(),
            userblock: 0,
            filebacked: false,
            increment: 64 * 1024 * 1024,
        }
    }

    pub fn driver<S: Into<String>>(&mut self, driver: S) -> &mut FileBuilder {
        self.driver = driver.into(); self
    }

    pub fn mode<S: Into<String>>(&mut self, mode: S) -> &mut FileBuilder {
        self.mode = mode.into(); self
    }

    pub fn userblock(&mut self, userblock: size_t) -> &mut FileBuilder {
        self.userblock = userblock; self
    }

    pub fn filebacked(&mut self, filebacked: bool) -> &mut FileBuilder {
        self.filebacked = filebacked; self
    }

    pub fn increment(&mut self, increment: size_t) -> &mut FileBuilder {
        self.increment = increment; self
    }

    fn make_fapl(&self) -> Result<PropertyList> {
        h5lock_s!({
            let fapl = PropertyList::from_id(h5try!(H5Pcreate(*H5P_FILE_ACCESS)));
            match self.driver.as_ref() {
                "sec2"  => h5try!(H5Pset_fapl_sec2(fapl.id())),
                "stdio" => h5try!(H5Pset_fapl_stdio(fapl.id())),
                "core"  => h5try!(H5Pset_fapl_core(fapl.id(), self.increment,
                                                   self.filebacked as hbool_t)),
                _       => fail!(format!("Invalid file driver: {}", self.driver)),
            };
            Ok(fapl)
        })
    }

    fn open_file(&self, write: bool) -> Result<File> {
        ensure!(self.userblock == 0, "Cannot specify userblock when opening a file");
        h5lock_s!({
            let fapl = try!(self.make_fapl());
            let flags = if write { H5F_ACC_RDWR } else { H5F_ACC_RDONLY };
            match self.filename.to_str() {
                Some(filename) => {
                    let c_filename = string_to_cstr(filename);
                    let file = File::from_id(h5try!(H5Fopen(c_filename, flags, fapl.id())));
                    ensure!(file.is_valid(), "Invalid id for opened file");
                    Ok(file)
                },
                None          => fail!("Invalid UTF-8 in file name: {:?}", self.filename)
            }
        })
    }

    fn create_file(&self, exclusive: bool) -> Result<File> {
        h5lock_s!({
            let fcpl = PropertyList::from_id(h5try!(H5Pcreate(*H5P_FILE_CREATE)));
            h5try!(H5Pset_userblock(fcpl.id(), self.userblock));
            let fapl = try!(self.make_fapl());
            let flags = if exclusive { H5F_ACC_EXCL } else { H5F_ACC_TRUNC };
            match self.filename.to_str() {
                Some(filename) => {
                    let c_filename = string_to_cstr(filename);
                    let file = File::from_id(h5try!(H5Fcreate(c_filename, flags,
                                                              fcpl.id(), fapl.id())));
                    ensure!(file.is_valid(), "Invalid id for created file");
                    Ok(file)
                },
                None          => fail!("Invalid UTF-8 in file name: {:?}", self.filename)
            }
        })
    }

    pub fn open(&self) -> Result<File> {
        match self.mode.as_ref() {
            "r"        => self.open_file(false),
            "r+"       => self.open_file(true),
            "w"        => self.create_file(false),
            "w-" | "x" => self.create_file(true),
            "a"        => match self.open_file(true) {
                            Ok(file) => Ok(file),
                            _        => self.create_file(true),
                          },
            _          => fail!("Invalid file access mode, expected r|r+|w|w-|x|a"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::File;
    use std::path::PathBuf;
    use tempdir::TempDir;

    fn with_tmpdir<F: Fn(PathBuf)>(f: F) {
        let dir = TempDir::new_in(".", "tmp").unwrap();
        let path = dir.path().to_path_buf();
        f(path);
    }

    #[test]
    pub fn test_invalid_mode() {
        with_tmpdir(|dir| {
            assert_err!(File::open(&dir, "foo"), "Invalid file access mode");
        })
    }

    #[test]
    pub fn test_is_read_only() {
        with_tmpdir(|dir| {
            let path = dir.join("foo.h5");
            assert!(!File::open(&path, "w").unwrap().is_read_only());
            assert!(File::open(&path, "r").unwrap().is_read_only());
            assert!(!File::open(&path, "r+").unwrap().is_read_only());
            assert!(!File::open(&path, "a").unwrap().is_read_only());
        });
        with_tmpdir(|dir| {
            assert!(!File::open(dir.join("foo.h"), "a").unwrap().is_read_only());
        });
        with_tmpdir(|dir| {
            assert!(!File::open(dir.join("foo.h"), "x").unwrap().is_read_only());
        });
    }

    #[test]
    pub fn test_invalid_filename() {
        with_tmpdir(|dir| {
            assert_err!(File::open(&dir, "r"), "unable to open file");
            assert_err!(File::open(&dir, "r+"), "unable to open file");
            assert_err!(File::open(&dir, "x"), "unable to create file");
            assert_err!(File::open(&dir, "w-"), "unable to create file");
            assert_err!(File::open(&dir, "w"), "unable to create file");
            assert_err!(File::open(&dir, "a"), "unable to create file");
        });
    }
}
<|endoftext|>


<commit_before>use std::convert::From;

use binding::global::RubySpecialConsts;
use types::{InternalValue, Value, ValueType};

use {Object, VerifiedObject};

/// `NilClass`
#[derive(Debug, PartialEq)]
pub struct NilClass {
    value: Value,
}

impl NilClass {
    /// Creates a new instance of `NilClass` (`nil`).
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{NilClass, Object, VM};
    /// # VM::init();
    ///
    /// assert!(NilClass::new().value().is_nil());
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// nil.nil? == true
    /// ```
    pub fn new() -> Self {
        Self::from(Value::from(RubySpecialConsts::Nil as InternalValue))
    }
}

impl From<Value> for NilClass {
    fn from(value: Value) -> Self {
        NilClass { value: value }
    }
}

impl Object for NilClass {
    #[inline]
    fn value(&self) -> Value {
        self.value
    }
}

impl VerifiedObject for NilClass {
    fn is_correct_type<T: Object>(object: &T) -> bool {
        object.value().ty() == ValueType::Nil
    }

    fn error_message() -> &'static str {
        "Error converting to NilClass"
    }
}
<commit_msg>Implement Default for NilClass<commit_after>use std::convert::From;
use std::default::Default;

use binding::global::RubySpecialConsts;
use types::{InternalValue, Value, ValueType};

use {Object, VerifiedObject};

/// `NilClass`
#[derive(Debug, PartialEq)]
pub struct NilClass {
    value: Value,
}

impl NilClass {
    /// Creates a new instance of `NilClass` (`nil`).
    ///
    /// # Examples
    ///
    /// ```
    /// use ruru::{NilClass, Object, VM};
    /// # VM::init();
    ///
    /// assert!(NilClass::new().value().is_nil());
    /// ```
    ///
    /// Ruby:
    ///
    /// ```ruby
    /// nil.nil? == true
    /// ```
    pub fn new() -> Self {
        Self::from(Value::from(RubySpecialConsts::Nil as InternalValue))
    }
}

impl Default for NilClass {
    fn default() -> Self {
        NilClass::new()
    }
}

impl From<Value> for NilClass {
    fn from(value: Value) -> Self {
        NilClass { value: value }
    }
}

impl Object for NilClass {
    #[inline]
    fn value(&self) -> Value {
        self.value
    }
}

impl VerifiedObject for NilClass {
    fn is_correct_type<T: Object>(object: &T) -> bool {
        object.value().ty() == ValueType::Nil
    }

    fn error_message() -> &'static str {
        "Error converting to NilClass"
    }
}
<|endoftext|>


<commit_before>extern crate num_cpus;
extern crate itertools;
extern crate rand;
extern crate crossbeam;

use self::rand::{thread_rng, Rng};
use self::itertools::Itertools;
use self::crossbeam::{scope, ScopedJoinHandle};

use std::ops::Range;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::sync::{Mutex, RwLock, MutexGuard};
use std::sync::mpsc::{Sender, Receiver, channel};
use std::thread::spawn;
use std::collections::BTreeSet;

use task::{TaskGenerator, Task};
use candidate::{WorkingCandidate, Candidate};
use context::Context;
use scaling::{ScalingFunction, proportionate};
use result::{Result as AbcResult, Error as AbcError};

/// Manages the parameters of the ABC algorithm.
pub struct HiveBuilder<Ctx: Context> {
    workers: usize,
    observers: usize,
    retries: usize,
    context: Ctx,
    threads: usize,
    scale: Box<ScalingFunction>,
}

impl<Ctx: Context> HiveBuilder<Ctx> {
    /// Creates a new hive.
    ///
    /// * `context` - Factory-like state that can be used while generating solutions.
    /// * `workers` - Number of working solution candidates to maintain at a time.
    pub fn new(context: Ctx, workers: usize) -> HiveBuilder<Ctx> {
        if workers == 0 {
            panic!("HiveBuilder must have at least one worker.");
        }

        HiveBuilder {
            workers: workers,
            observers: workers,
            retries: workers,

            context: context,
            threads: num_cpus::get(),
            scale: proportionate(),
        }
    }

    /// Sets the number of "bees" that will pick a candidate to work on at random.
    ///
    /// This defaults to the number of workers.
    pub fn set_observers(mut self, observers: usize) -> HiveBuilder<Ctx> {
        self.observers = observers;
        self
    }

    /// Sets the number of times a candidate can go unimproved before being reinitialized.
    ///
    /// This defaults to the number of workers.
    pub fn set_retries(mut self, retries: usize) -> HiveBuilder<Ctx> {
        self.retries = retries;
        self
    }

    /// Sets the number of worker threads to use while running.
    pub fn set_threads(mut self, threads: usize) -> HiveBuilder<Ctx> {
        self.threads = threads;
        self
    }

    /// Sets the scaling function for observers to use.
    pub fn set_scaling(mut self, scale: Box<ScalingFunction>) -> HiveBuilder<Ctx> {
        self.scale = scale;
        self
    }

    /// Activates the `HiveBuilder` to create a runnable object.
    pub fn build(self) -> AbcResult<Hive<Ctx>> {
        Hive::new(self)
    }

    fn new_candidate(&self) -> Candidate<Ctx::Solution> {
        let solution = self.context.make();
        let fitness = self.context.evaluate_fitness(&solution);
        Candidate::new(solution, fitness)
    }
}

/// Runs the ABC algorithm, maintaining any necessary state.
pub struct Hive<Ctx: Context> {
    hive: HiveBuilder<Ctx>,

    working: Vec<RwLock<WorkingCandidate<Ctx::Solution>>>,
    best: Mutex<Candidate<Ctx::Solution>>,
    scouting: RwLock<BTreeSet<usize>>,

    tasks: Mutex<Option<TaskGenerator>>,
    sender: Option<Mutex<Sender<Candidate<Ctx::Solution>>>>,
}

impl<Ctx: Context> Hive<Ctx> {
    fn new(hive: HiveBuilder<Ctx>) -> AbcResult<Hive<Ctx>> {
        // Start by populating the field with an initial set of solution candidates.

        // Feed the worker threads a total of N items, each signifying that
        // we need another candidate.
        let tokens: Mutex<Range<usize>> = Mutex::new(0..hive.workers);

        let candidates = Mutex::new(Vec::with_capacity(hive.workers));
        let mut handles = Vec::<ScopedJoinHandle<AbcResult<()>>>::with_capacity(hive.threads);

        try!(crossbeam::scope(|scope| {
            for _ in 0..hive.threads {
                handles.push(scope.spawn(|| {
                    while let Some(_) = tokens.lock().unwrap().next() {
                        let candidate = hive.new_candidate();
                        try!(candidates.lock()).push(candidate);
                    }
                    Ok(())
                }));
            }

            // Gather and return `Ok` iff all of the workers finished
            // successfully, otherwise abort the construction.
            handles.drain(..)
                   .fold(Ok(()), |result, handle| result.and(handle.join()))
        }));

        // We don't need the mutex anymore, since we're no longer populating
        // the candidate set from multiple threads.
        let mut candidates = try!(candidates.into_inner());

        // Find the current best candidate, since we want to cache the best
        // at any given moment.
        let best = {
            let best_candidate = candidates.iter()
                                           .fold1(|best, next| {
                                               if next.fitness > best.fitness {
                                                   next
                                               } else {
                                                   best
                                               }
                                           })
                                           .unwrap();
            Mutex::new(best_candidate.clone())
        };

        // Wrap the candidates in a structure that will let the eventual
        // thread swarm work on them.
        let working = candidates.drain(..)
                                .map(|c| RwLock::new(WorkingCandidate::new(c, hive.retries)))
                                .collect::<Vec<RwLock<WorkingCandidate<Ctx::Solution>>>>();

        Ok(Hive {
            hive: hive,
            working: working,
            best: best,
            scouting: RwLock::new(BTreeSet::new()),
            tasks: Mutex::new(None),
            sender: None,
        })
    }

    /// Clone a snapshot of the current set of working candidates.
    ///
    /// The goal of this function is to hold a guard for each solution for as
    /// little time as possible, so we can get out of the way of the other
    /// threads. To this end, we clone the solutions, so that the thread can do
    /// its work on a snapshot.
    fn current_working(&self) -> AbcResult<Vec<Candidate<Ctx::Solution>>> {
        let mut current_working = Vec::with_capacity(self.working.len());
        for candidate_mutex in &self.working {
            let read_guard = try!(candidate_mutex.read());
            current_working.push(read_guard.candidate.clone())
        }
        Ok(current_working)
    }

    /// Returns a guard for the current best solution found by the hive.
    ///
    /// If the hive is running, you should drop the guard returned by this
    /// function as soon as convenient, since the logic of the hive can block
    /// on the availability of the associated mutex. If you plan on performing
    /// expensive computations, you should `drop` the guard as soon as
    /// possible, or acquire and clone it within a small block.
    pub fn get(&self) -> AbcResult<MutexGuard<Candidate<Ctx::Solution>>> {
        self.best.lock().map_err(AbcError::from)
    }

    /// Perform greedy selection between a new candidate and the current best.
    fn consider_improvement(&self, candidate: &Candidate<Ctx::Solution>) -> AbcResult<()> {
        let mut best_guard = try!(self.best.lock());
        if candidate.fitness > best_guard.fitness {
            *best_guard = candidate.clone();
            if let Some(mutex) = self.sender.as_ref() {
                // We're streaming, so we need to post the improved candidate.
                let sender_guard = try!(mutex.lock());
                // If this errors, the receiver was dropped, so we're done.
                if let Err(_) = sender_guard.send(candidate.clone()) {
                    try!(self.stop());
                }
            }
        }
        Ok(())
    }

    fn work_on(&self, current_working: &[Candidate<Ctx::Solution>], n: usize) -> AbcResult<()> {
        let variant_solution = self.hive.context.explore(current_working, n);
        let variant_fitness = self.hive.context.evaluate_fitness(&variant_solution);
        let variant = Candidate::new(variant_solution, variant_fitness);
        let mut write_guard = try!(self.working[n].write());
        if variant.fitness > write_guard.candidate.fitness {
            *write_guard = WorkingCandidate::new(variant, self.hive.retries);
            try!(self.consider_improvement(&write_guard.candidate));
        } else {
            write_guard.deplete();
            // Scouting has been folded into the working process
            if write_guard.expired() {
                let mut scouting_guard = try!(self.scouting.write());
                scouting_guard.insert(n);
                drop(scouting_guard);

                let candidate = self.hive.new_candidate();
                *write_guard = WorkingCandidate::new(candidate, self.hive.retries);
                try!(self.consider_improvement(&write_guard.candidate));
                drop(write_guard);

                let mut scouting_guard = try!(self.scouting.write());
                scouting_guard.remove(&n);
            }
        }
        Ok(())
    }

    fn choose(&self, current_working: &[Candidate<Ctx::Solution>]) -> AbcResult<usize> {
        let fitnesses = (self.hive.scale)(current_working.iter()
                                                         .map(|candidate| candidate.fitness)
                                                         .collect::<Vec<f64>>());

        // Avoid observing candidates that are being scouted.
        let scouting_guard = try!(self.scouting.read());
        let running_totals = fitnesses.iter()
                                      .enumerate()
                                      .filter(|&(ref i, _)| !scouting_guard.contains(i))
                                      .scan(0f64, |total, (i, fitness)| {
                                          *total += *fitness;
                                          Some((i, *total))
                                      })
                                      .collect::<Vec<(usize, f64)>>();
        drop(scouting_guard);

        // Multiplying the choice point is equivalent to, and more efficient than, normalizing
        // all of the scaled fitnesses and having a choice point in [0,1)
        match running_totals.last() {
            Some(&(_, total_fitness)) => {
                let choice_point = thread_rng().next_f64() * total_fitness;
                for &(i, total) in running_totals.iter() {
                    if total > choice_point {
                        return Ok(i);
                    }
                }
                unreachable!();
            }

            // If we are currently scouting all of the solutions, pick one at random.
            None => Ok(thread_rng().gen_range::<usize>(0, fitnesses.len())),
        }
    }

    fn execute(&self, task: &Task) -> AbcResult<()> {
        let current_working = try!(self.current_working());
        let index = match *task {
            Task::Worker(n) => {
                // If the worker's candidate is in the middle of being replaced, just skip it.
                let scouting_guard = try!(self.scouting.read());
                if scouting_guard.contains(&n) {
                    return Ok(());
                }
                n
            }
            Task::Observer(_) => try!(self.choose(&current_working)),
        };
        self.work_on(&current_working, index)
    }

    fn run(&self, tasks: TaskGenerator) -> AbcResult<()> {
        let mut guard = try!(self.tasks.lock());
        *guard = Some(tasks);
        drop(guard);

        let mut handles: Vec<ScopedJoinHandle<AbcResult<()>>> = Vec::new();

        scope(|scope| {
            for _ in 0..self.hive.threads {
                handles.push(scope.spawn(|| {
                    loop {
                        let mut guard = try!(self.tasks.lock());
                        let task = guard.as_mut().and_then(|gen| gen.next());
                        drop(guard);

                        match task {
                            Some(t) => try!(self.execute(&t)),
                            None => return Ok(()),
                        };
                    }
                }));
            }

            // Returns `Ok(())` only if all threads join cleanly, and the task
            // cycle is successfully cleared away.
            //
            // We avoid `try!` because we want all of the following logic to
            // execute unconditionally.
            handles.drain(..)
                   .fold(Ok(()), |result, handle| result.and(handle.join()))
                   .and(self.tasks
                            .lock()
                            .map(|mut tasks_guard| *tasks_guard = None)
                            .map_err(AbcError::from))
        })
    }

    /// Runs for a fixed number of rounds, then return the best solution found.
    ///
    /// If one of the worker threads panics while working, this will return
    /// `Err(abc::Error)`. Otherwise, it will return `Ok` with a `Candidate`.
    pub fn run_for_rounds(&self, rounds: usize) -> AbcResult<Candidate<Ctx::Solution>> {
        let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers).max_rounds(rounds);
        try!(self.run(tasks));
        self.get().map(|guard| guard.clone())
    }

    /// Run indefinitely.
    ///
    /// If one of the worker threads panics while working, this will return
    /// `Err(abc::Error)`. Otherwise, it will return `Ok(())`.
    pub fn run_forever(&self) -> AbcResult<()> {
        let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers);
        self.run(tasks)
    }

    /// Stops a running hive.
    ///
    /// If a worker thread has panicked, this returns `Err(abc::Error)`.
    pub fn stop(&self) -> AbcResult<()> {
        let mut tasks_guard = try!(self.tasks.lock());
        Ok(tasks_guard.as_mut().map_or((), |t| t.stop()))
    }

    /// Each new best candidate will be sent to `sender`.
    ///
    /// This is kept in a separate function so that the hive can be borrowed
    /// while running.
    pub fn set_sender(&mut self, sender: Sender<Candidate<Ctx::Solution>>) {
        self.sender = Some(Mutex::new(sender));
    }

    /// Returns the current round of a running hive.
    ///
    /// If a worker thread has panicked and poisoned the task generator lock,
    /// `get_round` will return `Err(abc::Error)`. If the hive has not been
    /// run, `get_round` will return `Ok(None)`.
    ///
    /// If the hive is running, this will return `Ok(Some(n))`. `n` will start
    /// at 0, and increment each time every task in the round has been claimed
    /// (though not necessarily completed) by a worker thread.
    pub fn get_round(&self) -> AbcResult<Option<usize>> {
        let tasks_guard = try!(self.tasks.lock());
        Ok(tasks_guard.as_ref().map(|tasks| tasks.round))
    }

    /// Get a reference to the hive's context.
    pub fn context(&self) -> &Ctx {
        &self.hive.context
    }
}

impl<Ctx: Context + 'static> Hive<Ctx> {
    /// Runs indefinitely in the background, providing a stream of results.
    ///
    /// This method consumes the hive, which will run until the `HiveBuilder`
    /// object is dropped. It returns an `mpsc::Receiver`, which receives a
    /// `Candidate` each time the hive improves on its best solution.
    pub fn stream(mut self) -> Receiver<Candidate<Ctx::Solution>> {
        let (sender, receiver) = channel();
        spawn(move || {
            self.set_sender(sender);
            let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers);
            self.run(tasks)
        });
        receiver
    }
}

impl<Ctx: Context> Debug for Hive<Ctx>
    where Ctx::Solution: Debug
{
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        for mutex in (&self.working).iter() {
            let working = mutex.read().unwrap();
            try!(write!(f, "..{:?}..\n", working.candidate));
        }
        let best_candidate = self.get().unwrap();
        write!(f, ">>{:?}<<", *best_candidate)
    }
}

impl<Ctx: Context> Drop for Hive<Ctx> {
    fn drop(&mut self) {
        self.stop().unwrap_or(())
    }
}
<commit_msg>Fix bug where initialization was synchronous<commit_after>extern crate num_cpus;
extern crate itertools;
extern crate rand;
extern crate crossbeam;

use self::rand::{thread_rng, Rng};
use self::itertools::Itertools;
use self::crossbeam::{scope, ScopedJoinHandle};

use std::ops::Range;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::sync::{Mutex, RwLock, MutexGuard};
use std::sync::mpsc::{Sender, Receiver, channel};
use std::thread::spawn;
use std::collections::BTreeSet;

use task::{TaskGenerator, Task};
use candidate::{WorkingCandidate, Candidate};
use context::Context;
use scaling::{ScalingFunction, proportionate};
use result::{Result as AbcResult, Error as AbcError};

/// Manages the parameters of the ABC algorithm.
pub struct HiveBuilder<Ctx: Context> {
    workers: usize,
    observers: usize,
    retries: usize,
    context: Ctx,
    threads: usize,
    scale: Box<ScalingFunction>,
}

impl<Ctx: Context> HiveBuilder<Ctx> {
    /// Creates a new hive.
    ///
    /// * `context` - Factory-like state that can be used while generating solutions.
    /// * `workers` - Number of working solution candidates to maintain at a time.
    pub fn new(context: Ctx, workers: usize) -> HiveBuilder<Ctx> {
        if workers == 0 {
            panic!("HiveBuilder must have at least one worker.");
        }

        HiveBuilder {
            workers: workers,
            observers: workers,
            retries: workers,

            context: context,
            threads: num_cpus::get(),
            scale: proportionate(),
        }
    }

    /// Sets the number of "bees" that will pick a candidate to work on at random.
    ///
    /// This defaults to the number of workers.
    pub fn set_observers(mut self, observers: usize) -> HiveBuilder<Ctx> {
        self.observers = observers;
        self
    }

    /// Sets the number of times a candidate can go unimproved before being reinitialized.
    ///
    /// This defaults to the number of workers.
    pub fn set_retries(mut self, retries: usize) -> HiveBuilder<Ctx> {
        self.retries = retries;
        self
    }

    /// Sets the number of worker threads to use while running.
    pub fn set_threads(mut self, threads: usize) -> HiveBuilder<Ctx> {
        self.threads = threads;
        self
    }

    /// Sets the scaling function for observers to use.
    pub fn set_scaling(mut self, scale: Box<ScalingFunction>) -> HiveBuilder<Ctx> {
        self.scale = scale;
        self
    }

    /// Activates the `HiveBuilder` to create a runnable object.
    pub fn build(self) -> AbcResult<Hive<Ctx>> {
        Hive::new(self)
    }

    fn new_candidate(&self) -> Candidate<Ctx::Solution> {
        let solution = self.context.make();
        let fitness = self.context.evaluate_fitness(&solution);
        Candidate::new(solution, fitness)
    }
}

/// Runs the ABC algorithm, maintaining any necessary state.
pub struct Hive<Ctx: Context> {
    hive: HiveBuilder<Ctx>,

    working: Vec<RwLock<WorkingCandidate<Ctx::Solution>>>,
    best: Mutex<Candidate<Ctx::Solution>>,
    scouting: RwLock<BTreeSet<usize>>,

    tasks: Mutex<Option<TaskGenerator>>,
    sender: Option<Mutex<Sender<Candidate<Ctx::Solution>>>>,
}

impl<Ctx: Context> Hive<Ctx> {
    fn new(hive: HiveBuilder<Ctx>) -> AbcResult<Hive<Ctx>> {
        // Start by populating the field with an initial set of solution candidates.

        // Feed the worker threads a total of N items, each signifying that
        // we need another candidate.
        let tokens: Mutex<Range<usize>> = Mutex::new(0..hive.workers);

        let candidates = Mutex::new(Vec::with_capacity(hive.workers));
        let mut handles = Vec::<ScopedJoinHandle<AbcResult<()>>>::with_capacity(hive.threads);

        try!(crossbeam::scope(|scope| {
            for _ in 0..hive.threads {
                handles.push(scope.spawn(|| {
                    while let Some(_) = {
                        let mut guard = tokens.lock().unwrap();
                        guard.next()
                    } {
                        let candidate = hive.new_candidate();
                        try!(candidates.lock()).push(candidate);
                    }
                    Ok(())
                }));
            }

            // Gather and return `Ok` iff all of the workers finished
            // successfully, otherwise abort the construction.
            handles.drain(..)
                   .fold(Ok(()), |result, handle| result.and(handle.join()))
        }));

        // We don't need the mutex anymore, since we're no longer populating
        // the candidate set from multiple threads.
        let mut candidates = try!(candidates.into_inner());

        // Find the current best candidate, since we want to cache the best
        // at any given moment.
        let best = {
            let best_candidate = candidates.iter()
                                           .fold1(|best, next| {
                                               if next.fitness > best.fitness {
                                                   next
                                               } else {
                                                   best
                                               }
                                           })
                                           .unwrap();
            Mutex::new(best_candidate.clone())
        };

        // Wrap the candidates in a structure that will let the eventual
        // thread swarm work on them.
        let working = candidates.drain(..)
                                .map(|c| RwLock::new(WorkingCandidate::new(c, hive.retries)))
                                .collect::<Vec<RwLock<WorkingCandidate<Ctx::Solution>>>>();

        Ok(Hive {
            hive: hive,
            working: working,
            best: best,
            scouting: RwLock::new(BTreeSet::new()),
            tasks: Mutex::new(None),
            sender: None,
        })
    }

    /// Clone a snapshot of the current set of working candidates.
    ///
    /// The goal of this function is to hold a guard for each solution for as
    /// little time as possible, so we can get out of the way of the other
    /// threads. To this end, we clone the solutions, so that the thread can do
    /// its work on a snapshot.
    fn current_working(&self) -> AbcResult<Vec<Candidate<Ctx::Solution>>> {
        let mut current_working = Vec::with_capacity(self.working.len());
        for candidate_mutex in &self.working {
            let read_guard = try!(candidate_mutex.read());
            current_working.push(read_guard.candidate.clone())
        }
        Ok(current_working)
    }

    /// Returns a guard for the current best solution found by the hive.
    ///
    /// If the hive is running, you should drop the guard returned by this
    /// function as soon as convenient, since the logic of the hive can block
    /// on the availability of the associated mutex. If you plan on performing
    /// expensive computations, you should `drop` the guard as soon as
    /// possible, or acquire and clone it within a small block.
    pub fn get(&self) -> AbcResult<MutexGuard<Candidate<Ctx::Solution>>> {
        self.best.lock().map_err(AbcError::from)
    }

    /// Perform greedy selection between a new candidate and the current best.
    fn consider_improvement(&self, candidate: &Candidate<Ctx::Solution>) -> AbcResult<()> {
        let mut best_guard = try!(self.best.lock());
        if candidate.fitness > best_guard.fitness {
            *best_guard = candidate.clone();
            if let Some(mutex) = self.sender.as_ref() {
                // We're streaming, so we need to post the improved candidate.
                let sender_guard = try!(mutex.lock());
                // If this errors, the receiver was dropped, so we're done.
                if let Err(_) = sender_guard.send(candidate.clone()) {
                    try!(self.stop());
                }
            }
        }
        Ok(())
    }

    fn work_on(&self, current_working: &[Candidate<Ctx::Solution>], n: usize) -> AbcResult<()> {
        let variant_solution = self.hive.context.explore(current_working, n);
        let variant_fitness = self.hive.context.evaluate_fitness(&variant_solution);
        let variant = Candidate::new(variant_solution, variant_fitness);
        let mut write_guard = try!(self.working[n].write());
        if variant.fitness > write_guard.candidate.fitness {
            *write_guard = WorkingCandidate::new(variant, self.hive.retries);
            try!(self.consider_improvement(&write_guard.candidate));
        } else {
            write_guard.deplete();
            // Scouting has been folded into the working process
            if write_guard.expired() {
                let mut scouting_guard = try!(self.scouting.write());
                scouting_guard.insert(n);
                drop(scouting_guard);

                let candidate = self.hive.new_candidate();
                *write_guard = WorkingCandidate::new(candidate, self.hive.retries);
                try!(self.consider_improvement(&write_guard.candidate));
                drop(write_guard);

                let mut scouting_guard = try!(self.scouting.write());
                scouting_guard.remove(&n);
            }
        }
        Ok(())
    }

    fn choose(&self, current_working: &[Candidate<Ctx::Solution>]) -> AbcResult<usize> {
        let fitnesses = (self.hive.scale)(current_working.iter()
                                                         .map(|candidate| candidate.fitness)
                                                         .collect::<Vec<f64>>());

        // Avoid observing candidates that are being scouted.
        let scouting_guard = try!(self.scouting.read());
        let running_totals = fitnesses.iter()
                                      .enumerate()
                                      .filter(|&(ref i, _)| !scouting_guard.contains(i))
                                      .scan(0f64, |total, (i, fitness)| {
                                          *total += *fitness;
                                          Some((i, *total))
                                      })
                                      .collect::<Vec<(usize, f64)>>();
        drop(scouting_guard);

        // Multiplying the choice point is equivalent to, and more efficient than, normalizing
        // all of the scaled fitnesses and having a choice point in [0,1)
        match running_totals.last() {
            Some(&(_, total_fitness)) => {
                let choice_point = thread_rng().next_f64() * total_fitness;
                for &(i, total) in running_totals.iter() {
                    if total > choice_point {
                        return Ok(i);
                    }
                }
                unreachable!();
            }

            // If we are currently scouting all of the solutions, pick one at random.
            None => Ok(thread_rng().gen_range::<usize>(0, fitnesses.len())),
        }
    }

    fn execute(&self, task: &Task) -> AbcResult<()> {
        let current_working = try!(self.current_working());
        let index = match *task {
            Task::Worker(n) => {
                // If the worker's candidate is in the middle of being replaced, just skip it.
                let scouting_guard = try!(self.scouting.read());
                if scouting_guard.contains(&n) {
                    return Ok(());
                }
                n
            }
            Task::Observer(_) => try!(self.choose(&current_working)),
        };
        self.work_on(&current_working, index)
    }

    fn run(&self, tasks: TaskGenerator) -> AbcResult<()> {
        let mut guard = try!(self.tasks.lock());
        *guard = Some(tasks);
        drop(guard);

        let mut handles: Vec<ScopedJoinHandle<AbcResult<()>>> = Vec::new();

        scope(|scope| {
            for _ in 0..self.hive.threads {
                handles.push(scope.spawn(|| {
                    loop {
                        let mut guard = try!(self.tasks.lock());
                        let task = guard.as_mut().and_then(|gen| gen.next());
                        drop(guard);

                        match task {
                            Some(t) => try!(self.execute(&t)),
                            None => return Ok(()),
                        };
                    }
                }));
            }

            // Returns `Ok(())` only if all threads join cleanly, and the task
            // cycle is successfully cleared away.
            //
            // We avoid `try!` because we want all of the following logic to
            // execute unconditionally.
            handles.drain(..)
                   .fold(Ok(()), |result, handle| result.and(handle.join()))
                   .and(self.tasks
                            .lock()
                            .map(|mut tasks_guard| *tasks_guard = None)
                            .map_err(AbcError::from))
        })
    }

    /// Runs for a fixed number of rounds, then return the best solution found.
    ///
    /// If one of the worker threads panics while working, this will return
    /// `Err(abc::Error)`. Otherwise, it will return `Ok` with a `Candidate`.
    pub fn run_for_rounds(&self, rounds: usize) -> AbcResult<Candidate<Ctx::Solution>> {
        let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers).max_rounds(rounds);
        try!(self.run(tasks));
        self.get().map(|guard| guard.clone())
    }

    /// Run indefinitely.
    ///
    /// If one of the worker threads panics while working, this will return
    /// `Err(abc::Error)`. Otherwise, it will return `Ok(())`.
    pub fn run_forever(&self) -> AbcResult<()> {
        let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers);
        self.run(tasks)
    }

    /// Stops a running hive.
    ///
    /// If a worker thread has panicked, this returns `Err(abc::Error)`.
    pub fn stop(&self) -> AbcResult<()> {
        let mut tasks_guard = try!(self.tasks.lock());
        Ok(tasks_guard.as_mut().map_or((), |t| t.stop()))
    }

    /// Each new best candidate will be sent to `sender`.
    ///
    /// This is kept in a separate function so that the hive can be borrowed
    /// while running.
    pub fn set_sender(&mut self, sender: Sender<Candidate<Ctx::Solution>>) {
        self.sender = Some(Mutex::new(sender));
    }

    /// Returns the current round of a running hive.
    ///
    /// If a worker thread has panicked and poisoned the task generator lock,
    /// `get_round` will return `Err(abc::Error)`. If the hive has not been
    /// run, `get_round` will return `Ok(None)`.
    ///
    /// If the hive is running, this will return `Ok(Some(n))`. `n` will start
    /// at 0, and increment each time every task in the round has been claimed
    /// (though not necessarily completed) by a worker thread.
    pub fn get_round(&self) -> AbcResult<Option<usize>> {
        let tasks_guard = try!(self.tasks.lock());
        Ok(tasks_guard.as_ref().map(|tasks| tasks.round))
    }

    /// Get a reference to the hive's context.
    pub fn context(&self) -> &Ctx {
        &self.hive.context
    }
}

impl<Ctx: Context + 'static> Hive<Ctx> {
    /// Runs indefinitely in the background, providing a stream of results.
    ///
    /// This method consumes the hive, which will run until the `HiveBuilder`
    /// object is dropped. It returns an `mpsc::Receiver`, which receives a
    /// `Candidate` each time the hive improves on its best solution.
    pub fn stream(mut self) -> Receiver<Candidate<Ctx::Solution>> {
        let (sender, receiver) = channel();
        spawn(move || {
            self.set_sender(sender);
            let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers);
            self.run(tasks)
        });
        receiver
    }
}

impl<Ctx: Context> Debug for Hive<Ctx>
    where Ctx::Solution: Debug
{
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        for mutex in (&self.working).iter() {
            let working = mutex.read().unwrap();
            try!(write!(f, "..{:?}..\n", working.candidate));
        }
        let best_candidate = self.get().unwrap();
        write!(f, ">>{:?}<<", *best_candidate)
    }
}

impl<Ctx: Context> Drop for Hive<Ctx> {
    fn drop(&mut self) {
        self.stop().unwrap_or(())
    }
}
<|endoftext|>