<commit_before>//! The `arrange` operator and its variants arrange a collection into a shareable trace structure.
//!
//! The `arrange` operator is applied to a differential dataflow `Collection` and return an `Arranged`.
//! Several operators (`join`, `group`, and `cogroup`, among others) are implemented in terms of this
//! structure, and can be applied directly to arranged data. Internally, the operators will borrow the
//! shared state, and listen on the timely stream for shared batches of data. The resources to assemble
//! the collection, both computation and memory, are spent only once.
//!
//! The arranged collection is stored in a trace, whose append-only behavior means that it is safe to
//! share between the single writer and multiple readers. Each reader is expected to interrogate the
//! trace only at times for which it knows the trace is complete, as indicated by the frontiers on its
//! incoming channels. Failing to do this is "safe", but may result in undefined semantics.
//!
//! Internally, the shared trace is wrapped in a `TraceWrapper` type which maintains information about
//! the frontiers of all of its referees. Each referee has a `TraceHandle`, which acts as a reference
//! counted pointer, and which mediates the advancement of frontiers. Ideally, a `TraceHandle` looks a
//! lot like a trace, though this isn't beatifully masked at the moment (it can't implement the trait
//! because we can't insert at it; it does implement `advance_by` and could implement `cursor`).
//!
//! Note: in the current implementation, all batches have only one time. This will probably change and
//! shouldn't be relied on.

use std::rc::Rc;
use std::cell::RefCell;
use std::default::Default;

use linear_map::LinearMap;

use timely::dataflow::*;
use timely::dataflow::operators::Unary;
use timely::dataflow::channels::pact::{Pipeline, Exchange};
use timely::progress::frontier::MutableAntichain;

use ::{Data, Collection, AsCollection};
use lattice::Lattice;
use trace::{Trace, Batch, Builder, Cursor};
use trace::implementations::trie::Spine as TrieSpine;
use trace::implementations::keys::Spine as KeysSpine;

/// Wrapper type to permit transfer of `Rc` types, as in batch.
#[derive(Clone)]
pub struct BatchWrapper<T> {
    /// The wrapped item.
    pub item: T,
}

// NOTE: This is all horrible. Don't look too hard.
impl<T> ::abomonation::Abomonation for BatchWrapper<T> {
   unsafe fn entomb(&self, _writer: &mut Vec<u8>) { panic!() }
   unsafe fn embalm(&mut self) { panic!() }
   unsafe fn exhume<'a,'b>(&'a mut self, _bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { panic!()  }
}

/// A wrapper around a trace which tracks the frontiers of all referees.
pub struct TraceWrapper<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> {
    phantom: ::std::marker::PhantomData<(K, V)>,
    frontiers: MutableAntichain<T>,
    /// The wrapped trace.
    pub trace: Tr,
}

impl<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> TraceWrapper<K, V, T, Tr> {
    /// Allocates a new trace wrapper.
    pub fn new(default: T) -> Self {
        TraceWrapper {
            phantom: ::std::marker::PhantomData,
            frontiers: MutableAntichain::new(),
            trace: Tr::new(default)
        }
    }
    /// Reports the current frontier of the trace.
    fn frontier(&self) -> &[T] { self.frontiers.elements() }
    /// Replaces elements of `lower` with those of `upper`.
    fn adjust_frontier(&mut self, lower: &[T], upper: &[T]) {
        for element in upper { self.frontiers.update_and(element, 1, |_,_| {}); }
        for element in lower { self.frontiers.update_and(element, -1, |_,_| {}); }
        self.trace.advance_by(self.frontiers.elements());
    }
}

/// A handle to a shared trace which maintains its own frontier information.
///
/// As long as the handle exists, it should protect the trace from advancing past the associated frontier.
/// This protection advances as the handle's frontier is advanced. When the handle is dropped the protection
/// is removed.
pub struct TraceHandle<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> {
    phantom: ::std::marker::PhantomData<(K, V)>,
    frontier: Vec<T>,
    /// Wrapped trace. Please be gentle when using.
    pub wrapper: Rc<RefCell<TraceWrapper<K, V, T, Tr>>>,
}

impl<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> TraceHandle<K, V, T, Tr> {
    /// Allocates a new handle from an existing wrapped wrapper.
    pub fn new(wrapper: &Rc<RefCell<TraceWrapper<K, V, T, Tr>>>) -> Self {
        let frontier = wrapper.borrow().frontier().to_vec();
        wrapper.borrow_mut().adjust_frontier(&[], &frontier[..]);
        TraceHandle {
            phantom: ::std::marker::PhantomData,
            frontier: frontier,
            wrapper: wrapper.clone(),
        }
    }
    /// Sets frontier to now be elements in `frontier`.
    pub fn advance_by(&mut self, frontier: &[T]) {
        self.wrapper.borrow_mut().adjust_frontier(&self.frontier[..], frontier);
        self.frontier = frontier.to_vec();
    }
    /// Creates a new cursor over the wrapped trace.
    pub fn cursor(&self) -> Tr::Cursor {
        self.wrapper.borrow().trace.cursor()
    }
}

impl<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> Drop for TraceHandle<K, V, T, Tr> {
    fn drop(&mut self) {
        self.wrapper.borrow_mut().adjust_frontier(&self.frontier[..], &[]);
        self.frontier = Vec::new();
    }
}

/// A collection of `(K,V)` values as a timely stream and shared trace.
///
/// An `Arranged` performs the task of arranging a keyed collection once,
/// allowing multiple differential operators to use the same trace. This
/// saves on computation and memory, in exchange for some cognitive overhead
/// in writing differential operators: each must pay enough care to signals
/// from the `stream` field to know the subset of `trace` it has logically
/// received.
pub struct Arranged<G: Scope, K: Data+Ord, V: Data+Ord, T: Trace<K, V, G::Timestamp>>
    where G::Timestamp: Lattice+Ord {
    /// A stream containing arranged updates.
    ///
    /// This stream may be reduced to just the keys, with the expectation that looking up the
    /// updated values in the associated trace should be more efficient than cloning the values.
    /// Users who need the values can obviously use the original stream.
    pub stream: Stream<G, BatchWrapper<T::Batch>>,
    /// A shared trace, updated by the `Arrange` operator and readable by others.
    pub trace: Rc<RefCell<TraceWrapper<K, V, G::Timestamp, T>>>,
    /// If dereferenced, we build this collection.
    pub collection: Option<Collection<G, (K, V)>>,
}

impl<G: Scope, K: Data+Ord, V: Data+Ord, T: Trace<K, V, G::Timestamp>> Arranged<G, K, V, T>
    where G::Timestamp: Lattice+Ord,
          T::Batch: Clone+'static {

    /// Allocates a new handle to the shared trace, with independent frontier tracking.
    pub fn new_handle(&self) -> TraceHandle<K, V, G::Timestamp, T> {
        TraceHandle::new(&self.trace)
    }

    /// Flattens the stream into a `Collection`.
    ///
    /// This operator is not obviously more efficient than using the `Collection` that is input
    /// to the `Arrange` operator. However, this may be the only way to recover a collection from
    /// only the `Arranged` type.
    pub fn as_collection(&self) -> Collection<G, (K, V)> {

        // unimplemented!()

        self.stream.unary_stream(Pipeline, "AsCollection", |input, output| {
            input.for_each(|time, data| {
                let mut session = output.session(&time);
                for wrapper in data.drain(..) {
                    let batch = wrapper.item;
                    let mut cursor = batch.cursor();
                    while cursor.key_valid() {
                        while cursor.val_valid() {
                            cursor.map_times(|_time, diff| {
                                session.give(((cursor.key().clone(), cursor.val().clone()), diff));
                            });
                            cursor.step_val();
                        }
                        cursor.step_key();
                    }
                }
            })
        })
        .as_collection()
    }
}

// impl<G: Scope, K: Data+Ord, V: Data+Ord, T: Trace<K, V, G::Timestamp>> Deref for Arranged<G, K, V, T>
//     where G::Timestamp: Lattice+Ord,
//           T::Batch: Clone+'static {
//     type Target = Collection<G, (K, V)>;
//     fn deref(&self) -> &Self::Target {
//         if self.collection.is_none() {
//             self.collection = Some(self.as_collection());
//         }
//         self.collection.as_ref().unwrap()
//     }
// }

/// Arranges something as `(Key,Val)` pairs according to a type `T` of trace.
pub trait Arrange<G: Scope, K: Data+Ord, V: Data+Ord, T: Trace<K, V, G::Timestamp>>
where G::Timestamp: Lattice+Ord,
      T::Batch: Clone+'static {
    /// Arranges a stream of `(Key, Val)` updates by `Key`.
    ///
    /// This operator arranges a stream of values into a shared trace, whose contents it maintains.
    /// This trace is current for all times completed by the output stream, which can be used to
    /// safely identify the stable times and values in the trace.
    fn arrange(&self) -> Arranged<G, K, V, T>;
}

impl<G: Scope, K: Data+Ord, V: Data+Ord, T: Trace<K, V, G::Timestamp>+'static> Arrange<G, K, V, T> for Collection<G, (K, V)>
where G::Timestamp: Lattice+Ord,
      T::Batch: Clone+'static {
    fn arrange(&self) -> Arranged<G, K, V, T> {

        // create a trace to share with downstream consumers.
        let trace = Rc::new(RefCell::new(TraceWrapper::new(Default::default())));
        let source = Rc::downgrade(&trace);

        // A map from times to received (key, val, wgt) triples.
        let mut inputs = LinearMap::new();

        // fabricate a data-parallel operator using the `unary_notify` pattern.
        let exchange = Exchange::new(|x: &((K,V),isize)| (x.0).0.hashed());
        let stream = self.inner.unary_notify(exchange, "ArrangeByKey", vec![], move |input, output, notificator| {

            input.for_each(|time, data| {
                inputs.entry(time.time())
                      .or_insert_with(|| { notificator.notify_at(time.clone()); <T::Batch as Batch<K,V,G::Timestamp>>::Builder::new() })
                      .extend(data.drain(..).map(|(kv,d)| (kv.0, kv.1, time.time(), d)));
            });

            notificator.for_each(|index, _count, _notificator| {
                if let Some(builder) = inputs.remove(&index) {
                    let batch = builder.done(&[], &[]);
                    source.upgrade().map(|trace| {
                        let trace: &mut T = &mut trace.borrow_mut().trace;
                        trace.insert(batch.clone())
                    });
                    output.session(&index).give(BatchWrapper { item: batch });
                }
            });
        });

        Arranged { stream: stream, trace: trace, collection: None }
    }
}

/// Arranges something as `(Key,Val)` pairs according to a type `T` of trace.
pub trait ArrangeByKey<G: Scope, K: Data+Ord, V: Data+Ord>
where G::Timestamp: Lattice+Ord {
    /// Arranges a stream of `(Key, Val)` updates by `Key`.
    ///
    /// This operator arranges a stream of values into a shared trace, whose contents it maintains.
    /// This trace is current for all times completed by the output stream, which can be used to
    /// safely identify the stable times and values in the trace.
    fn arrange_by_key(&self) -> Arranged<G, K, V, TrieSpine<K, V, G::Timestamp>>;
}

impl<G: Scope, K: Data+Ord, V: Data+Ord> ArrangeByKey<G, K, V> for Collection<G, (K,V)>
where G::Timestamp: Lattice+Ord  {

    fn arrange_by_key(&self) -> Arranged<G, K, V, TrieSpine<K, V, G::Timestamp>> {
        self.arrange()
    }
}

/// Arranges something as `(Key,Val)` pairs according to a type `T` of trace.
pub trait ArrangeBySelf<G: Scope, K: Data+Ord>
where G::Timestamp: Lattice+Ord {
    /// Arranges a stream of `(Key, Val)` updates by `Key`.
    ///
    /// This operator arranges a stream of values into a shared trace, whose contents it maintains.
    /// This trace is current for all times completed by the output stream, which can be used to
    /// safely identify the stable times and values in the trace.
    fn arrange_by_self(&self) -> Arranged<G, K, (), KeysSpine<K, G::Timestamp>>;
}

impl<G: Scope, K: Data+Ord> ArrangeBySelf<G, K> for Collection<G, K>
where G::Timestamp: Lattice+Ord {
    fn arrange_by_self(&self) -> Arranged<G, K, (), KeysSpine<K, G::Timestamp>> {
        self.map(|k| (k,()))
            .arrange()
    }
}
<commit_msg>remove Ord constraint where redundant<commit_after>//! The `arrange` operator and its variants arrange a collection into a shareable trace structure.
//!
//! The `arrange` operator is applied to a differential dataflow `Collection` and return an `Arranged`.
//! Several operators (`join`, `group`, and `cogroup`, among others) are implemented in terms of this
//! structure, and can be applied directly to arranged data. Internally, the operators will borrow the
//! shared state, and listen on the timely stream for shared batches of data. The resources to assemble
//! the collection, both computation and memory, are spent only once.
//!
//! The arranged collection is stored in a trace, whose append-only behavior means that it is safe to
//! share between the single writer and multiple readers. Each reader is expected to interrogate the
//! trace only at times for which it knows the trace is complete, as indicated by the frontiers on its
//! incoming channels. Failing to do this is "safe", but may result in undefined semantics.
//!
//! Internally, the shared trace is wrapped in a `TraceWrapper` type which maintains information about
//! the frontiers of all of its referees. Each referee has a `TraceHandle`, which acts as a reference
//! counted pointer, and which mediates the advancement of frontiers. Ideally, a `TraceHandle` looks a
//! lot like a trace, though this isn't beatifully masked at the moment (it can't implement the trait
//! because we can't insert at it; it does implement `advance_by` and could implement `cursor`).
//!
//! Note: in the current implementation, all batches have only one time. This will probably change and
//! shouldn't be relied on.

use std::rc::Rc;
use std::cell::RefCell;
use std::default::Default;

use linear_map::LinearMap;

use timely::dataflow::*;
use timely::dataflow::operators::Unary;
use timely::dataflow::channels::pact::{Pipeline, Exchange};
use timely::progress::frontier::MutableAntichain;

use ::{Data, Collection, AsCollection};
use lattice::Lattice;
use trace::{Trace, Batch, Builder, Cursor};
use trace::implementations::trie::Spine as TrieSpine;
use trace::implementations::keys::Spine as KeysSpine;

/// Wrapper type to permit transfer of `Rc` types, as in batch.
#[derive(Clone)]
pub struct BatchWrapper<T> {
    /// The wrapped item.
    pub item: T,
}

// NOTE: This is all horrible. Don't look too hard.
impl<T> ::abomonation::Abomonation for BatchWrapper<T> {
   unsafe fn entomb(&self, _writer: &mut Vec<u8>) { panic!() }
   unsafe fn embalm(&mut self) { panic!() }
   unsafe fn exhume<'a,'b>(&'a mut self, _bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { panic!()  }
}

/// A wrapper around a trace which tracks the frontiers of all referees.
pub struct TraceWrapper<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> {
    phantom: ::std::marker::PhantomData<(K, V)>,
    frontiers: MutableAntichain<T>,
    /// The wrapped trace.
    pub trace: Tr,
}

impl<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> TraceWrapper<K, V, T, Tr> {
    /// Allocates a new trace wrapper.
    pub fn new(default: T) -> Self {
        TraceWrapper {
            phantom: ::std::marker::PhantomData,
            frontiers: MutableAntichain::new(),
            trace: Tr::new(default)
        }
    }
    /// Reports the current frontier of the trace.
    fn frontier(&self) -> &[T] { self.frontiers.elements() }
    /// Replaces elements of `lower` with those of `upper`.
    fn adjust_frontier(&mut self, lower: &[T], upper: &[T]) {
        for element in upper { self.frontiers.update_and(element, 1, |_,_| {}); }
        for element in lower { self.frontiers.update_and(element, -1, |_,_| {}); }
        self.trace.advance_by(self.frontiers.elements());
    }
}

/// A handle to a shared trace which maintains its own frontier information.
///
/// As long as the handle exists, it should protect the trace from advancing past the associated frontier.
/// This protection advances as the handle's frontier is advanced. When the handle is dropped the protection
/// is removed.
pub struct TraceHandle<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> {
    phantom: ::std::marker::PhantomData<(K, V)>,
    frontier: Vec<T>,
    /// Wrapped trace. Please be gentle when using.
    pub wrapper: Rc<RefCell<TraceWrapper<K, V, T, Tr>>>,
}

impl<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> TraceHandle<K, V, T, Tr> {
    /// Allocates a new handle from an existing wrapped wrapper.
    pub fn new(wrapper: &Rc<RefCell<TraceWrapper<K, V, T, Tr>>>) -> Self {
        let frontier = wrapper.borrow().frontier().to_vec();
        wrapper.borrow_mut().adjust_frontier(&[], &frontier[..]);
        TraceHandle {
            phantom: ::std::marker::PhantomData,
            frontier: frontier,
            wrapper: wrapper.clone(),
        }
    }
    /// Sets frontier to now be elements in `frontier`.
    pub fn advance_by(&mut self, frontier: &[T]) {
        self.wrapper.borrow_mut().adjust_frontier(&self.frontier[..], frontier);
        self.frontier = frontier.to_vec();
    }
    /// Creates a new cursor over the wrapped trace.
    pub fn cursor(&self) -> Tr::Cursor {
        self.wrapper.borrow().trace.cursor()
    }
}

impl<K: Ord, V: Ord, T: Lattice+Ord+Clone+::std::fmt::Debug+'static, Tr: Trace<K, V, T>> Drop for TraceHandle<K, V, T, Tr> {
    fn drop(&mut self) {
        self.wrapper.borrow_mut().adjust_frontier(&self.frontier[..], &[]);
        self.frontier = Vec::new();
    }
}

/// A collection of `(K,V)` values as a timely stream and shared trace.
///
/// An `Arranged` performs the task of arranging a keyed collection once,
/// allowing multiple differential operators to use the same trace. This
/// saves on computation and memory, in exchange for some cognitive overhead
/// in writing differential operators: each must pay enough care to signals
/// from the `stream` field to know the subset of `trace` it has logically
/// received.
pub struct Arranged<G: Scope, K: Data+Ord, V: Data+Ord, T: Trace<K, V, G::Timestamp>>
    where G::Timestamp: Lattice+Ord {
    /// A stream containing arranged updates.
    ///
    /// This stream may be reduced to just the keys, with the expectation that looking up the
    /// updated values in the associated trace should be more efficient than cloning the values.
    /// Users who need the values can obviously use the original stream.
    pub stream: Stream<G, BatchWrapper<T::Batch>>,
    /// A shared trace, updated by the `Arrange` operator and readable by others.
    pub trace: Rc<RefCell<TraceWrapper<K, V, G::Timestamp, T>>>,
    /// If dereferenced, we build this collection.
    pub collection: Option<Collection<G, (K, V)>>,
}

impl<G: Scope, K: Data+Ord, V: Data+Ord, T: Trace<K, V, G::Timestamp>> Arranged<G, K, V, T>
    where G::Timestamp: Lattice+Ord,
          T::Batch: Clone+'static {

    /// Allocates a new handle to the shared trace, with independent frontier tracking.
    pub fn new_handle(&self) -> TraceHandle<K, V, G::Timestamp, T> {
        TraceHandle::new(&self.trace)
    }

    /// Flattens the stream into a `Collection`.
    ///
    /// This operator is not obviously more efficient than using the `Collection` that is input
    /// to the `Arrange` operator. However, this may be the only way to recover a collection from
    /// only the `Arranged` type.
    pub fn as_collection(&self) -> Collection<G, (K, V)> {

        self.stream.unary_stream(Pipeline, "AsCollection", |input, output| {
            input.for_each(|time, data| {
                let mut session = output.session(&time);
                for wrapper in data.drain(..) {
                    let batch = wrapper.item;
                    let mut cursor = batch.cursor();
                    while cursor.key_valid() {
                        while cursor.val_valid() {
                            cursor.map_times(|_time, diff| {
                                session.give(((cursor.key().clone(), cursor.val().clone()), diff));
                            });
                            cursor.step_val();
                        }
                        cursor.step_key();
                    }
                }
            })
        })
        .as_collection()
    }
}

/// Arranges something as `(Key,Val)` pairs according to a type `T` of trace.
pub trait Arrange<G: Scope, K: Data, V: Data, T: Trace<K, V, G::Timestamp>>
where G::Timestamp: Lattice+Ord,
      T::Batch: Clone+'static {
    /// Arranges a stream of `(Key, Val)` updates by `Key`.
    ///
    /// This operator arranges a stream of values into a shared trace, whose contents it maintains.
    /// This trace is current for all times completed by the output stream, which can be used to
    /// safely identify the stable times and values in the trace.
    fn arrange(&self) -> Arranged<G, K, V, T>;
}

impl<G: Scope, K: Data, V: Data, T: Trace<K, V, G::Timestamp>+'static> Arrange<G, K, V, T> for Collection<G, (K, V)>
where G::Timestamp: Lattice+Ord,
      T::Batch: Clone+'static {
    fn arrange(&self) -> Arranged<G, K, V, T> {

        // create a trace to share with downstream consumers.
        let trace = Rc::new(RefCell::new(TraceWrapper::new(Default::default())));
        let source = Rc::downgrade(&trace);

        // A map from times to received (key, val, wgt) triples.
        let mut inputs = LinearMap::new();

        // fabricate a data-parallel operator using the `unary_notify` pattern.
        let exchange = Exchange::new(|x: &((K,V),isize)| (x.0).0.hashed());
        let stream = self.inner.unary_notify(exchange, "ArrangeByKey", vec![], move |input, output, notificator| {

            input.for_each(|time, data| {
                inputs.entry(time.time())
                      .or_insert_with(|| {
                        notificator.notify_at(time.clone());
                        <T::Batch as Batch<K,V,G::Timestamp>>::Builder::new()
                      })
                      .extend(data.drain(..).map(|(kv,d)| (kv.0, kv.1, time.time(), d)));
            });

            notificator.for_each(|index, _count, _notificator| {
                if let Some(builder) = inputs.remove(&index) {
                    let batch = builder.done(&[], &[]);
                    source.upgrade().map(|trace| {
                        let trace: &mut T = &mut trace.borrow_mut().trace;
                        trace.insert(batch.clone())
                    });
                    output.session(&index).give(BatchWrapper { item: batch });
                }
            });
        });

        Arranged { stream: stream, trace: trace, collection: None }
    }
}

/// Arranges something as `(Key,Val)` pairs according to a type `T` of trace.
pub trait ArrangeByKey<G: Scope, K: Data+Ord, V: Data+Ord>
where G::Timestamp: Lattice+Ord {
    /// Arranges a stream of `(Key, Val)` updates by `Key`.
    ///
    /// This operator arranges a stream of values into a shared trace, whose contents it maintains.
    /// This trace is current for all times completed by the output stream, which can be used to
    /// safely identify the stable times and values in the trace.
    fn arrange_by_key(&self) -> Arranged<G, K, V, TrieSpine<K, V, G::Timestamp>>;
}

impl<G: Scope, K: Data+Ord, V: Data+Ord> ArrangeByKey<G, K, V> for Collection<G, (K,V)>
where G::Timestamp: Lattice+Ord  {

    fn arrange_by_key(&self) -> Arranged<G, K, V, TrieSpine<K, V, G::Timestamp>> {
        self.arrange()
    }
}

/// Arranges something as `(Key,Val)` pairs according to a type `T` of trace.
pub trait ArrangeBySelf<G: Scope, K: Data+Ord>
where G::Timestamp: Lattice+Ord {
    /// Arranges a stream of `(Key, Val)` updates by `Key`.
    ///
    /// This operator arranges a stream of values into a shared trace, whose contents it maintains.
    /// This trace is current for all times completed by the output stream, which can be used to
    /// safely identify the stable times and values in the trace.
    fn arrange_by_self(&self) -> Arranged<G, K, (), KeysSpine<K, G::Timestamp>>;
}

impl<G: Scope, K: Data+Ord> ArrangeBySelf<G, K> for Collection<G, K>
where G::Timestamp: Lattice+Ord {
    fn arrange_by_self(&self) -> Arranged<G, K, (), KeysSpine<K, G::Timestamp>> {
        self.map(|k| (k,()))
            .arrange()
    }
}
<|endoftext|>


<commit_before>use proc_macro::TokenStream;
use std::io::Write;
use std::process;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

const DEBUG_NOTE: &str = ":  use --release
  Syn's test suite has some tests that run on every source file
  and test case in the rust-lang/rust repo, which can be pretty
  slow in debug mode. Consider running cargo test with `--release`
  to speed things up.";

const FEATURES_ERROR: &str = ":  use --all-features
  Syn's test suite normally only works with all-features enabled.
  Run again with `--all-features`, or run with `--features test`
  to bypass this check.";

#[proc_macro]
pub fn check(_input: TokenStream) -> TokenStream {
    let ref mut stderr = StandardStream::stderr(ColorChoice::Auto);

    if cfg!(debug_assertions) {
        let yellow = ColorSpec::new().set_fg(Some(Color::Yellow)).clone();
        let _ = writeln!(stderr);
        let _ = stderr.set_color(yellow.clone().set_bold(true));
        let _ = write!(stderr, "NOTE");
        let _ = stderr.set_color(&yellow);
        let _ = writeln!(stderr, "{}", DEBUG_NOTE);
        let _ = stderr.reset();
    }

    if cfg!(not(feature = "all-features")) {
        let red = ColorSpec::new().set_fg(Some(Color::Red)).clone();
        let _ = writeln!(stderr);
        let _ = stderr.set_color(red.clone().set_bold(true));
        let _ = write!(stderr, "ERROR");
        let _ = stderr.set_color(&red);
        let _ = writeln!(stderr, "{}", FEATURES_ERROR);
        let _ = stderr.reset();
        process::exit(1);
    }

    TokenStream::new()
}
<commit_msg>Color each line of test suite checker output<commit_after>use proc_macro::TokenStream;
use std::io::Write;
use std::process;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

const DEBUG_NOTE: &str = ":  use --release
  Syn's test suite has some tests that run on every source file
  and test case in the rust-lang/rust repo, which can be pretty
  slow in debug mode. Consider running cargo test with `--release`
  to speed things up.";

const FEATURES_ERROR: &str = ":  use --all-features
  Syn's test suite normally only works with all-features enabled.
  Run again with `--all-features`, or run with `--features test`
  to bypass this check.";

#[proc_macro]
pub fn check(_input: TokenStream) -> TokenStream {
    let ref mut stderr = StandardStream::stderr(ColorChoice::Auto);

    if cfg!(debug_assertions) {
        let yellow = ColorSpec::new().set_fg(Some(Color::Yellow)).clone();
        let _ = writeln!(stderr);
        let _ = stderr.set_color(yellow.clone().set_bold(true));
        let _ = write!(stderr, "NOTE");
        for line in DEBUG_NOTE.lines() {
            let _ = stderr.set_color(&yellow);
            let _ = writeln!(stderr, "{}", line);
        }
        let _ = stderr.reset();
    }

    if cfg!(not(feature = "all-features")) {
        let red = ColorSpec::new().set_fg(Some(Color::Red)).clone();
        let _ = writeln!(stderr);
        let _ = stderr.set_color(red.clone().set_bold(true));
        let _ = write!(stderr, "ERROR");
        for line in FEATURES_ERROR.lines() {
            let _ = stderr.set_color(&red);
            let _ = writeln!(stderr, "{}", line);
        }
        let _ = stderr.reset();
        process::exit(1);
    }

    TokenStream::new()
}
<|endoftext|>


<commit_before>use chrono::{DateTime, Utc};
use float_cmp::approx_eq;
use lopdf::{self, Dictionary, Object};
use predicates::prelude::*;
use predicates::reflection::{Case, Child, PredicateReflection, Product};
use std::cmp;
use std::fmt;

/// Checks that the variable of type [u8] can be parsed as a PDF file.
#[derive(Debug)]
pub struct PdfPredicate {}

impl PdfPredicate {
    pub fn with_page_count(self: Self, num_pages: usize) -> DetailPredicate<Self> {
        DetailPredicate::<Self> {
            p: self,
            d: Detail::PageCount(num_pages),
        }
    }

    pub fn with_page_size(
        self: Self,
        idx: usize,
        width_in_points: f64,
        height_in_points: f64,
    ) -> DetailPredicate<Self> {
        DetailPredicate::<Self> {
            p: self,
            d: Detail::PageSize(
                Dimensions {
                    w: width_in_points,
                    h: height_in_points,
                    unit: 1.0,
                },
                idx,
            ),
        }
    }

    pub fn with_creation_date(self: Self, when: DateTime<Utc>) -> DetailPredicate<Self> {
        DetailPredicate::<Self> {
            p: self,
            d: Detail::CreationDate(when),
        }
    }

    pub fn with_link(self: Self, link: &str) -> DetailPredicate<Self> {
        DetailPredicate::<Self> {
            p: self,
            d: Detail::Link(link.to_string()),
        }
    }
}

impl Predicate<[u8]> for PdfPredicate {
    fn eval(&self, data: &[u8]) -> bool {
        lopdf::Document::load_mem(data).is_ok()
    }

    fn find_case<'a>(&'a self, _expected: bool, data: &[u8]) -> Option<Case<'a>> {
        match lopdf::Document::load_mem(data) {
            Ok(_) => None,
            Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
        }
    }
}

impl PredicateReflection for PdfPredicate {}

impl fmt::Display for PdfPredicate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "is a PDF")
    }
}

/// Extends a PdfPredicate by a check for page count, page size or creation date.
#[derive(Debug)]
pub struct DetailPredicate<PdfPredicate> {
    p: PdfPredicate,
    d: Detail,
}

#[derive(Debug)]
enum Detail {
    PageCount(usize),
    PageSize(Dimensions, usize),
    CreationDate(DateTime<Utc>),
    Link(String),
}

/// A PDF page's dimensions from its `MediaBox`.
///
/// Note that `w` and `h` given in `UserUnit`, which is by default 1.0 = 1/72 inch.
#[derive(Debug)]
struct Dimensions {
    w: f64,
    h: f64,
    unit: f64, // UserUnit, in points (1/72 of an inch)
}

impl Dimensions {
    pub fn from_media_box(obj: &lopdf::Object, unit: Option<f64>) -> lopdf::Result<Dimensions> {
        let a = obj.as_array()?;
        Ok(Dimensions {
            w: a[2].as_float()?,
            h: a[3].as_float()?,
            unit: unit.unwrap_or(1.0),
        })
    }

    pub fn width_in_pt(self: &Self) -> f64 {
        self.w * self.unit
    }

    pub fn height_in_pt(self: &Self) -> f64 {
        self.h * self.unit
    }
}

impl fmt::Display for Dimensions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} pt x {} pt", self.width_in_pt(), self.height_in_pt())
    }
}

impl cmp::PartialEq for Dimensions {
    fn eq(&self, other: &Self) -> bool {
        approx_eq!(
            f64,
            self.width_in_pt(),
            other.width_in_pt(),
            epsilon = 0.000_001
        ) && approx_eq!(
            f64,
            self.height_in_pt(),
            other.height_in_pt(),
            epsilon = 0.000_001
        )
    }
}

impl cmp::Eq for Dimensions {}

trait Details {
    fn get_page_count(&self) -> usize;
    fn get_page_size(&self, idx: usize) -> Option<Dimensions>;
    fn get_creation_date(&self) -> Option<DateTime<Utc>>;
    fn get_from_trailer<'a>(self: &'a Self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object>;
    fn get_from_page<'a>(
        self: &'a Self,
        idx: usize,
        key: &[u8],
    ) -> lopdf::Result<&'a lopdf::Object>;
}

impl DetailPredicate<PdfPredicate> {
    fn eval_doc(&self, doc: &lopdf::Document) -> bool {
        match &self.d {
            Detail::PageCount(n) => doc.get_page_count() == *n,
            Detail::PageSize(d, idx) => doc.get_page_size(*idx).map_or(false, |dim| dim == *d),
            Detail::CreationDate(d) => doc.get_creation_date().map_or(false, |date| date == *d),
            Detail::Link(link) => document_has_link(doc, &link),
        }
    }

    fn find_case_for_doc<'a>(&'a self, expected: bool, doc: &lopdf::Document) -> Option<Case<'a>> {
        if self.eval_doc(doc) == expected {
            let product = self.product_for_doc(doc);
            Some(Case::new(Some(self), false).add_product(product))
        } else {
            None
        }
    }

    fn product_for_doc(&self, doc: &lopdf::Document) -> Product {
        match &self.d {
            Detail::PageCount(_) => Product::new(
                "actual page count",
                format!("{} page(s)", doc.get_page_count()),
            ),
            Detail::PageSize(_, idx) => Product::new(
                "actual page size",
                match doc.get_page_size(*idx) {
                    Some(dim) => format!("{}", dim),
                    None => "None".to_string(),
                },
            ),
            Detail::CreationDate(_) => Product::new(
                "actual creation date",
                format!("{:?}", doc.get_creation_date()),
            ),
            Detail::Link(_) => Product::new(
                "actual link contents",
                "FIXME: who knows, but it's not what we expected".to_string(),
            ),
        }
    }
}

// Extensions to lopdf::Object; can be removed after lopdf 0.26
trait ObjExt {
    /// Get the object value as a float.
    /// Unlike as_f64() this will also cast an Integer to a Real.
    fn as_float(&self) -> lopdf::Result<f64>;
}

impl ObjExt for lopdf::Object {
    fn as_float(&self) -> lopdf::Result<f64> {
        match *self {
            lopdf::Object::Integer(ref value) => Ok(*value as f64),
            lopdf::Object::Real(ref value) => Ok(*value),
            _ => Err(lopdf::Error::Type),
        }
    }
}

impl Details for lopdf::Document {
    fn get_page_count(self: &Self) -> usize {
        self.get_pages().len()
    }

    fn get_page_size(self: &Self, idx: usize) -> Option<Dimensions> {
        match self.get_from_page(idx, b"MediaBox") {
            Ok(obj) => {
                let unit = self
                    .get_from_page(idx, b"UserUnit")
                    .and_then(ObjExt::as_float)
                    .ok();
                Dimensions::from_media_box(obj, unit).ok()
            }
            Err(_) => None,
        }
    }

    fn get_creation_date(self: &Self) -> Option<DateTime<Utc>> {
        match self.get_from_trailer(b"CreationDate") {
            Ok(obj) => obj.as_datetime().map(|date| date.with_timezone(&Utc)),
            Err(_) => None,
        }
    }

    fn get_from_trailer<'a>(self: &'a Self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object> {
        let id = self.trailer.get(b"Info")?.as_reference()?;
        self.get_object(id)?.as_dict()?.get(key)
    }

    fn get_from_page<'a>(
        self: &'a Self,
        idx: usize,
        key: &[u8],
    ) -> lopdf::Result<&'a lopdf::Object> {
        let mut iter = self.page_iter();
        for _ in 0..idx {
            let _ = iter.next();
        }
        match iter.next() {
            Some(id) => self.get_object(id)?.as_dict()?.get(key),
            None => Err(lopdf::Error::ObjectNotFound),
        }
    }
}

impl Predicate<[u8]> for DetailPredicate<PdfPredicate> {
    fn eval(&self, data: &[u8]) -> bool {
        match lopdf::Document::load_mem(data) {
            Ok(doc) => self.eval_doc(&doc),
            _ => false,
        }
    }

    fn find_case<'a>(&'a self, expected: bool, data: &[u8]) -> Option<Case<'a>> {
        match lopdf::Document::load_mem(data) {
            Ok(doc) => self.find_case_for_doc(expected, &doc),
            Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
        }
    }
}

impl PredicateReflection for DetailPredicate<PdfPredicate> {
    fn children<'a>(&'a self) -> Box<dyn Iterator<Item = Child<'a>> + 'a> {
        let params = vec![Child::new("predicate", &self.p)];
        Box::new(params.into_iter())
    }
}

impl fmt::Display for DetailPredicate<PdfPredicate> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.d {
            Detail::PageCount(n) => write!(f, "is a PDF with {} page(s)", n),
            Detail::PageSize(d, _) => write!(f, "is a PDF sized {}", d),
            Detail::CreationDate(d) => write!(f, "is a PDF created {:?}", d),
            Detail::Link(l) => write!(f, "is a PDF with a link to {}", l),
        }
    }
}

// We do a super simple test that a PDF actually contains an Annotation object
// with a particular link.  We don't test that this annotation is actually linked
// from a page; that would be nicer.
fn document_has_link(document: &lopdf::Document, link_text: &str) -> bool {
    document
        .objects
        .iter()
        .map(|(_obj_id, object)| object)
        .any(|obj| object_is_annotation_with_link(obj, link_text))
}

fn object_is_annotation_with_link(object: &Object, link_text: &str) -> bool {
    object
        .as_dict()
        .map(|dict| dict_is_annotation(dict) && dict_has_a_with_link(dict, link_text))
        .unwrap_or(false)
}

fn dict_is_annotation(dict: &Dictionary) -> bool {
    dict.get(b"Type")
        .and_then(|type_val| type_val.as_name_str())
        .map(|name| name == "Annot")
        .unwrap_or(false)
}

fn dict_has_a_with_link(dict: &Dictionary, link_text: &str) -> bool {
    dict.get(b"A")
        .and_then(|obj| obj.as_dict())
        .and_then(|dict| dict.get(b"URI"))
        .and_then(|obj| obj.as_str())
        .map(|string| string == link_text.as_bytes())
        .unwrap_or(false)
}
<commit_msg>PdfPredicate.with_text() - simple way to test for a PDF containing some textual content<commit_after>use chrono::{DateTime, Utc};
use float_cmp::approx_eq;
use lopdf::{self, Dictionary, Object};
use predicates::prelude::*;
use predicates::reflection::{Case, Child, PredicateReflection, Product};
use std::cmp;
use std::fmt;

/// Checks that the variable of type [u8] can be parsed as a PDF file.
#[derive(Debug)]
pub struct PdfPredicate {}

impl PdfPredicate {
    pub fn with_page_count(self: Self, num_pages: usize) -> DetailPredicate<Self> {
        DetailPredicate::<Self> {
            p: self,
            d: Detail::PageCount(num_pages),
        }
    }

    pub fn with_page_size(
        self: Self,
        idx: usize,
        width_in_points: f64,
        height_in_points: f64,
    ) -> DetailPredicate<Self> {
        DetailPredicate::<Self> {
            p: self,
            d: Detail::PageSize(
                Dimensions {
                    w: width_in_points,
                    h: height_in_points,
                    unit: 1.0,
                },
                idx,
            ),
        }
    }

    pub fn with_creation_date(self: Self, when: DateTime<Utc>) -> DetailPredicate<Self> {
        DetailPredicate::<Self> {
            p: self,
            d: Detail::CreationDate(when),
        }
    }

    pub fn with_link(self: Self, link: &str) -> DetailPredicate<Self> {
        DetailPredicate::<Self> {
            p: self,
            d: Detail::Link(link.to_string()),
        }
    }

    pub fn with_text(self: Self, text: &str) -> DetailPredicate<Self> {
        DetailPredicate::<Self> {
            p: self,
            d: Detail::Text(text.to_string()),
        }
    }
}

impl Predicate<[u8]> for PdfPredicate {
    fn eval(&self, data: &[u8]) -> bool {
        lopdf::Document::load_mem(data).is_ok()
    }

    fn find_case<'a>(&'a self, _expected: bool, data: &[u8]) -> Option<Case<'a>> {
        match lopdf::Document::load_mem(data) {
            Ok(_) => None,
            Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
        }
    }
}

impl PredicateReflection for PdfPredicate {}

impl fmt::Display for PdfPredicate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "is a PDF")
    }
}

/// Extends a PdfPredicate by a check for page count, page size or creation date.
#[derive(Debug)]
pub struct DetailPredicate<PdfPredicate> {
    p: PdfPredicate,
    d: Detail,
}

#[derive(Debug)]
enum Detail {
    PageCount(usize),
    PageSize(Dimensions, usize),
    CreationDate(DateTime<Utc>),
    Link(String),
    Text(String),
}

/// A PDF page's dimensions from its `MediaBox`.
///
/// Note that `w` and `h` given in `UserUnit`, which is by default 1.0 = 1/72 inch.
#[derive(Debug)]
struct Dimensions {
    w: f64,
    h: f64,
    unit: f64, // UserUnit, in points (1/72 of an inch)
}

impl Dimensions {
    pub fn from_media_box(obj: &lopdf::Object, unit: Option<f64>) -> lopdf::Result<Dimensions> {
        let a = obj.as_array()?;
        Ok(Dimensions {
            w: a[2].as_float()?,
            h: a[3].as_float()?,
            unit: unit.unwrap_or(1.0),
        })
    }

    pub fn width_in_pt(self: &Self) -> f64 {
        self.w * self.unit
    }

    pub fn height_in_pt(self: &Self) -> f64 {
        self.h * self.unit
    }
}

impl fmt::Display for Dimensions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} pt x {} pt", self.width_in_pt(), self.height_in_pt())
    }
}

impl cmp::PartialEq for Dimensions {
    fn eq(&self, other: &Self) -> bool {
        approx_eq!(
            f64,
            self.width_in_pt(),
            other.width_in_pt(),
            epsilon = 0.000_001
        ) && approx_eq!(
            f64,
            self.height_in_pt(),
            other.height_in_pt(),
            epsilon = 0.000_001
        )
    }
}

impl cmp::Eq for Dimensions {}

trait Details {
    fn get_page_count(&self) -> usize;
    fn get_page_size(&self, idx: usize) -> Option<Dimensions>;
    fn get_creation_date(&self) -> Option<DateTime<Utc>>;
    fn get_from_trailer<'a>(self: &'a Self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object>;
    fn get_from_page<'a>(
        self: &'a Self,
        idx: usize,
        key: &[u8],
    ) -> lopdf::Result<&'a lopdf::Object>;
}

impl DetailPredicate<PdfPredicate> {
    fn eval_doc(&self, doc: &lopdf::Document) -> bool {
        match &self.d {
            Detail::PageCount(n) => doc.get_page_count() == *n,
            Detail::PageSize(d, idx) => doc.get_page_size(*idx).map_or(false, |dim| dim == *d),
            Detail::CreationDate(d) => doc.get_creation_date().map_or(false, |date| date == *d),
            Detail::Link(link) => document_has_link(doc, &link),
            Detail::Text(text) => document_has_text(doc, &text),
        }
    }

    fn find_case_for_doc<'a>(&'a self, expected: bool, doc: &lopdf::Document) -> Option<Case<'a>> {
        if self.eval_doc(doc) == expected {
            let product = self.product_for_doc(doc);
            Some(Case::new(Some(self), false).add_product(product))
        } else {
            None
        }
    }

    fn product_for_doc(&self, doc: &lopdf::Document) -> Product {
        match &self.d {
            Detail::PageCount(_) => Product::new(
                "actual page count",
                format!("{} page(s)", doc.get_page_count()),
            ),
            Detail::PageSize(_, idx) => Product::new(
                "actual page size",
                match doc.get_page_size(*idx) {
                    Some(dim) => format!("{}", dim),
                    None => "None".to_string(),
                },
            ),
            Detail::CreationDate(_) => Product::new(
                "actual creation date",
                format!("{:?}", doc.get_creation_date()),
            ),
            Detail::Link(_) => Product::new(
                "actual link contents",
                "FIXME: who knows, but it's not what we expected".to_string(),
            ),
            Detail::Text(_) => {
                Product::new("actual text contents", doc.extract_text(&[1]).unwrap())
            }
        }
    }
}

// Extensions to lopdf::Object; can be removed after lopdf 0.26
trait ObjExt {
    /// Get the object value as a float.
    /// Unlike as_f64() this will also cast an Integer to a Real.
    fn as_float(&self) -> lopdf::Result<f64>;
}

impl ObjExt for lopdf::Object {
    fn as_float(&self) -> lopdf::Result<f64> {
        match *self {
            lopdf::Object::Integer(ref value) => Ok(*value as f64),
            lopdf::Object::Real(ref value) => Ok(*value),
            _ => Err(lopdf::Error::Type),
        }
    }
}

impl Details for lopdf::Document {
    fn get_page_count(self: &Self) -> usize {
        self.get_pages().len()
    }

    fn get_page_size(self: &Self, idx: usize) -> Option<Dimensions> {
        match self.get_from_page(idx, b"MediaBox") {
            Ok(obj) => {
                let unit = self
                    .get_from_page(idx, b"UserUnit")
                    .and_then(ObjExt::as_float)
                    .ok();
                Dimensions::from_media_box(obj, unit).ok()
            }
            Err(_) => None,
        }
    }

    fn get_creation_date(self: &Self) -> Option<DateTime<Utc>> {
        match self.get_from_trailer(b"CreationDate") {
            Ok(obj) => obj.as_datetime().map(|date| date.with_timezone(&Utc)),
            Err(_) => None,
        }
    }

    fn get_from_trailer<'a>(self: &'a Self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object> {
        let id = self.trailer.get(b"Info")?.as_reference()?;
        self.get_object(id)?.as_dict()?.get(key)
    }

    fn get_from_page<'a>(
        self: &'a Self,
        idx: usize,
        key: &[u8],
    ) -> lopdf::Result<&'a lopdf::Object> {
        let mut iter = self.page_iter();
        for _ in 0..idx {
            let _ = iter.next();
        }
        match iter.next() {
            Some(id) => self.get_object(id)?.as_dict()?.get(key),
            None => Err(lopdf::Error::ObjectNotFound),
        }
    }
}

impl Predicate<[u8]> for DetailPredicate<PdfPredicate> {
    fn eval(&self, data: &[u8]) -> bool {
        match lopdf::Document::load_mem(data) {
            Ok(doc) => self.eval_doc(&doc),
            _ => false,
        }
    }

    fn find_case<'a>(&'a self, expected: bool, data: &[u8]) -> Option<Case<'a>> {
        match lopdf::Document::load_mem(data) {
            Ok(doc) => self.find_case_for_doc(expected, &doc),
            Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
        }
    }
}

impl PredicateReflection for DetailPredicate<PdfPredicate> {
    fn children<'a>(&'a self) -> Box<dyn Iterator<Item = Child<'a>> + 'a> {
        let params = vec![Child::new("predicate", &self.p)];
        Box::new(params.into_iter())
    }
}

impl fmt::Display for DetailPredicate<PdfPredicate> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.d {
            Detail::PageCount(n) => write!(f, "is a PDF with {} page(s)", n),
            Detail::PageSize(d, _) => write!(f, "is a PDF sized {}", d),
            Detail::CreationDate(d) => write!(f, "is a PDF created {:?}", d),
            Detail::Link(l) => write!(f, "is a PDF with a link to {}", l),
            Detail::Text(t) => write!(f, "is a PDF with \"{}\" in its text content", t),
        }
    }
}

// This is an extremely trivial test for a string being present in the document's
// text objects.
fn document_has_text(document: &lopdf::Document, needle: &str) -> bool {
    if let Ok(haystack) = text_from_first_page(document) {
        haystack.contains(needle)
    } else {
        false
    }
}

// We do a super simple test that a PDF actually contains an Annotation object
// with a particular link.  We don't test that this annotation is actually linked
// from a page; that would be nicer.
fn document_has_link(document: &lopdf::Document, link_text: &str) -> bool {
    document
        .objects
        .iter()
        .map(|(_obj_id, object)| object)
        .any(|obj| object_is_annotation_with_link(obj, link_text))
}

fn object_is_annotation_with_link(object: &Object, link_text: &str) -> bool {
    object
        .as_dict()
        .map(|dict| dict_is_annotation(dict) && dict_has_a_with_link(dict, link_text))
        .unwrap_or(false)
}

fn dict_is_annotation(dict: &Dictionary) -> bool {
    dict.get(b"Type")
        .and_then(|type_val| type_val.as_name_str())
        .map(|name| name == "Annot")
        .unwrap_or(false)
}

fn dict_has_a_with_link(dict: &Dictionary, link_text: &str) -> bool {
    dict.get(b"A")
        .and_then(|obj| obj.as_dict())
        .and_then(|dict| dict.get(b"URI"))
        .and_then(|obj| obj.as_str())
        .map(|string| string == link_text.as_bytes())
        .unwrap_or(false)
}

fn text_from_first_page(doc: &lopdf::Document) -> lopdf::Result<String> {
    // This is extremely simplistic; lopdf just concatenates all the text in the page
    // into a single string.
    doc.extract_text(&[1])
}
<|endoftext|>


<commit_before>//! Tests for profiles.

use std::env;

use cargo_test_support::project;

#[cargo_test]
fn profile_overrides() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = 1
            debug = false
            rpath = true
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C opt-level=1[..]\
        -C debug-assertions=on \
        -C metadata=[..] \
        -C rpath \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] dev [optimized] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn opt_level_override_0() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = 0
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn debug_override_1() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            debug = 1
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C debuginfo=1 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
        )
        .run();
}

fn check_opt_level_override(profile_level: &str, rustc_level: &str) {
    let p = project()
        .file(
            "Cargo.toml",
            &format!(
                r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = {level}
        "#,
                level = profile_level
            ),
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(&format!(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link \
        -C opt-level={level}[..]\
        -C debuginfo=2 \
        -C debug-assertions=on \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
            level = rustc_level
        ))
        .run();
}

#[cargo_test]
fn opt_level_overrides() {
    for &(profile_level, rustc_level) in &[
        ("1", "1"),
        ("2", "2"),
        ("3", "3"),
        ("\"s\"", "s"),
        ("\"z\"", "z"),
    ] {
        check_opt_level_override(profile_level, rustc_level)
    }
}

#[cargo_test]
fn top_level_overrides_deps() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.release]
            opt-level = 1
            debug = true

            [dependencies.foo]
            path = "foo"
        "#,
        )
        .file("src/lib.rs", "")
        .file(
            "foo/Cargo.toml",
            r#"
            [package]

            name = "foo"
            version = "0.0.0"
            authors = []

            [profile.release]
            opt-level = 0
            debug = false

            [lib]
            name = "foo"
            crate_type = ["dylib", "rlib"]
        "#,
        )
        .file("foo/src/lib.rs", "")
        .build();
    p.cargo("build -v --release")
        .with_stderr(&format!(
            "\
[COMPILING] foo v0.0.0 ([CWD]/foo)
[RUNNING] `rustc --crate-name foo foo/src/lib.rs [..]\
        --crate-type dylib --crate-type rlib \
        --emit=[..]link \
        -C prefer-dynamic \
        -C opt-level=1[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [CWD]/target/release/deps \
        -L dependency=[CWD]/target/release/deps`
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link \
        -C opt-level=1[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/release/deps \
        --extern foo=[CWD]/target/release/deps/\
                     {prefix}foo[..]{suffix} \
        --extern foo=[CWD]/target/release/deps/libfoo.rlib`
[FINISHED] release [optimized + debuginfo] target(s) in [..]
",
            prefix = env::consts::DLL_PREFIX,
            suffix = env::consts::DLL_SUFFIX
        ))
        .run();
}

#[cargo_test]
fn profile_in_non_root_manifest_triggers_a_warning() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [workspace]
            members = ["bar"]

            [profile.dev]
            debug = false
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []
            workspace = ".."

            [profile.dev]
            opt-level = 1
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    p.cargo("build -v")
        .cwd("bar")
        .with_stderr(
            "\
[WARNING] profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   [..]
workspace: [..]
[COMPILING] bar v0.1.0 ([..])
[RUNNING] `rustc [..]`
[FINISHED] dev [unoptimized] target(s) in [..]",
        )
        .run();
}

#[cargo_test]
fn profile_in_virtual_manifest_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [workspace]
            members = ["bar"]

            [profile.dev]
            opt-level = 1
            debug = false
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []
            workspace = ".."
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    p.cargo("build -v")
        .cwd("bar")
        .with_stderr(
            "\
[COMPILING] bar v0.1.0 ([..])
[RUNNING] `rustc [..]`
[FINISHED] dev [optimized] target(s) in [..]",
        )
        .run();
}

#[cargo_test]
fn profile_panic_test_bench() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"

            [profile.test]
            panic = "abort"

            [profile.bench]
            panic = "abort"
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build")
        .with_stderr_contains(
            "\
[WARNING] `panic` setting is ignored for `bench` profile
[WARNING] `panic` setting is ignored for `test` profile
",
        )
        .run();
}

#[cargo_test]
fn profile_doc_deprecated() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"

            [profile.doc]
            opt-level = 0
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build")
        .with_stderr_contains("[WARNING] profile `doc` is deprecated and has no effect")
        .run();
}

#[cargo_test]
fn panic_unwind_does_not_build_twice() {
    // Check for a bug where `lib` was built twice, once with panic set and
    // once without. Since "unwind" is the default, they are the same and
    // should only be built once.
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [profile.dev]
            panic = "unwind"
            "#,
        )
        .file("src/lib.rs", "")
        .file("src/main.rs", "fn main() {}")
        .file("tests/t1.rs", "")
        .build();

    p.cargo("test -v --tests --no-run")
        .with_stderr_unordered(
            "\
[COMPILING] foo [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..] --test [..]
[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin [..]
[RUNNING] `rustc --crate-name foo src/main.rs [..] --test [..]
[RUNNING] `rustc --crate-name t1 tests/t1.rs [..]
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn debug_0_report() {
    // The finished line handles 0 correctly.
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [profile.dev]
            debug = 0
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C debuginfo=0 [..]
[FINISHED] dev [unoptimized] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn thin_lto_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "top"
            version = "0.5.0"
            authors = []

            [profile.release]
            lto = 'thin'
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build --release -v")
        .with_stderr(
            "\
[COMPILING] top [..]
[RUNNING] `rustc [..] -C lto=thin [..]`
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn strip_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            cargo-features = ["strip"]

            [package]
            name = "foo"
            version = "0.1.0"

            [profile.release]
            strip = 'symbols'
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build --release -v")
        .masquerade_as_nightly_cargo()
        .with_stderr(
            "\
[COMPILING] foo [..]
[RUNNING] `rustc [..] -Z strip=symbols [..]`
[FINISHED] [..]
",
        )
        .run();
}
<commit_msg>Add more tests<commit_after>//! Tests for profiles.

use std::env;

use cargo_test_support::{is_nightly, project};

#[cargo_test]
fn profile_overrides() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = 1
            debug = false
            rpath = true
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C opt-level=1[..]\
        -C debug-assertions=on \
        -C metadata=[..] \
        -C rpath \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] dev [optimized] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn opt_level_override_0() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = 0
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn debug_override_1() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            debug = 1
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C debuginfo=1 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
        )
        .run();
}

fn check_opt_level_override(profile_level: &str, rustc_level: &str) {
    let p = project()
        .file(
            "Cargo.toml",
            &format!(
                r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = {level}
        "#,
                level = profile_level
            ),
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(&format!(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link \
        -C opt-level={level}[..]\
        -C debuginfo=2 \
        -C debug-assertions=on \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
            level = rustc_level
        ))
        .run();
}

#[cargo_test]
fn opt_level_overrides() {
    for &(profile_level, rustc_level) in &[
        ("1", "1"),
        ("2", "2"),
        ("3", "3"),
        ("\"s\"", "s"),
        ("\"z\"", "z"),
    ] {
        check_opt_level_override(profile_level, rustc_level)
    }
}

#[cargo_test]
fn top_level_overrides_deps() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.release]
            opt-level = 1
            debug = true

            [dependencies.foo]
            path = "foo"
        "#,
        )
        .file("src/lib.rs", "")
        .file(
            "foo/Cargo.toml",
            r#"
            [package]

            name = "foo"
            version = "0.0.0"
            authors = []

            [profile.release]
            opt-level = 0
            debug = false

            [lib]
            name = "foo"
            crate_type = ["dylib", "rlib"]
        "#,
        )
        .file("foo/src/lib.rs", "")
        .build();
    p.cargo("build -v --release")
        .with_stderr(&format!(
            "\
[COMPILING] foo v0.0.0 ([CWD]/foo)
[RUNNING] `rustc --crate-name foo foo/src/lib.rs [..]\
        --crate-type dylib --crate-type rlib \
        --emit=[..]link \
        -C prefer-dynamic \
        -C opt-level=1[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [CWD]/target/release/deps \
        -L dependency=[CWD]/target/release/deps`
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link \
        -C opt-level=1[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/release/deps \
        --extern foo=[CWD]/target/release/deps/\
                     {prefix}foo[..]{suffix} \
        --extern foo=[CWD]/target/release/deps/libfoo.rlib`
[FINISHED] release [optimized + debuginfo] target(s) in [..]
",
            prefix = env::consts::DLL_PREFIX,
            suffix = env::consts::DLL_SUFFIX
        ))
        .run();
}

#[cargo_test]
fn profile_in_non_root_manifest_triggers_a_warning() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [workspace]
            members = ["bar"]

            [profile.dev]
            debug = false
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []
            workspace = ".."

            [profile.dev]
            opt-level = 1
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    p.cargo("build -v")
        .cwd("bar")
        .with_stderr(
            "\
[WARNING] profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   [..]
workspace: [..]
[COMPILING] bar v0.1.0 ([..])
[RUNNING] `rustc [..]`
[FINISHED] dev [unoptimized] target(s) in [..]",
        )
        .run();
}

#[cargo_test]
fn profile_in_virtual_manifest_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [workspace]
            members = ["bar"]

            [profile.dev]
            opt-level = 1
            debug = false
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []
            workspace = ".."
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    p.cargo("build -v")
        .cwd("bar")
        .with_stderr(
            "\
[COMPILING] bar v0.1.0 ([..])
[RUNNING] `rustc [..]`
[FINISHED] dev [optimized] target(s) in [..]",
        )
        .run();
}

#[cargo_test]
fn profile_panic_test_bench() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"

            [profile.test]
            panic = "abort"

            [profile.bench]
            panic = "abort"
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build")
        .with_stderr_contains(
            "\
[WARNING] `panic` setting is ignored for `bench` profile
[WARNING] `panic` setting is ignored for `test` profile
",
        )
        .run();
}

#[cargo_test]
fn profile_doc_deprecated() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"

            [profile.doc]
            opt-level = 0
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build")
        .with_stderr_contains("[WARNING] profile `doc` is deprecated and has no effect")
        .run();
}

#[cargo_test]
fn panic_unwind_does_not_build_twice() {
    // Check for a bug where `lib` was built twice, once with panic set and
    // once without. Since "unwind" is the default, they are the same and
    // should only be built once.
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [profile.dev]
            panic = "unwind"
            "#,
        )
        .file("src/lib.rs", "")
        .file("src/main.rs", "fn main() {}")
        .file("tests/t1.rs", "")
        .build();

    p.cargo("test -v --tests --no-run")
        .with_stderr_unordered(
            "\
[COMPILING] foo [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..] --test [..]
[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin [..]
[RUNNING] `rustc --crate-name foo src/main.rs [..] --test [..]
[RUNNING] `rustc --crate-name t1 tests/t1.rs [..]
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn debug_0_report() {
    // The finished line handles 0 correctly.
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [profile.dev]
            debug = 0
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C debuginfo=0 [..]
[FINISHED] dev [unoptimized] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn thin_lto_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "top"
            version = "0.5.0"
            authors = []

            [profile.release]
            lto = 'thin'
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build --release -v")
        .with_stderr(
            "\
[COMPILING] top [..]
[RUNNING] `rustc [..] -C lto=thin [..]`
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn strip_works() {
    if !is_nightly() {
        return;
    }

    let p = project()
        .file(
            "Cargo.toml",
            r#"
            cargo-features = ["strip"]

            [package]
            name = "foo"
            version = "0.1.0"

            [profile.release]
            strip = 'symbols'
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build --release -v")
        .masquerade_as_nightly_cargo()
        .with_stderr(
            "\
[COMPILING] foo [..]
[RUNNING] `rustc [..] -Z strip=symbols [..]`
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn strip_requires_cargo_feature() {
    if !is_nightly() {
        return;
    }

    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [profile.release]
            strip = 'symbols'
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build --release -v")
        .masquerade_as_nightly_cargo()
        .with_status(101)
        .with_stderr(
            "\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`

Caused by:
  feature `strip` is required

consider adding `cargo-features = [\"strip\"]` to the manifest
",
        )
        .run();
}
#[cargo_test]
fn strip_rejects_invalid_option() {
    if !is_nightly() {
        return;
    }

    let p = project()
        .file(
            "Cargo.toml",
            r#"
            cargo-features = ["strip"]

            [package]
            name = "foo"
            version = "0.1.0"

            [profile.release]
            strip = 'wrong'
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build --release -v")
        .masquerade_as_nightly_cargo()
        .with_status(101)
        .with_stderr(
            "\
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`

Caused by:
  unknown variant `wrong`, expected one of `debuginfo`, `none`, `symbols` for key [..]
",
        )
        .run();
}
<|endoftext|>


<commit_before>extern crate graphics;
extern crate freetype as ft;
extern crate sdl2_window;
extern crate opengl_graphics;
extern crate piston;

use std::rc::Rc;
use std::cell::RefCell;
use std::path::Path;
use sdl2_window::Sdl2Window;
use opengl_graphics::{
    Gl,
    Texture,
    OpenGL
};
use graphics::vecmath::Matrix2d;
use graphics::{
    default_draw_state,
    Image,
    RelativeTransform,
    color
};
use piston::window::WindowSettings;
use piston::event::Event;

fn render_text(face: &mut ft::Face, gl: &mut Gl, t: Matrix2d, text: &str) {
    let mut x = 0;
    let mut y = 0;
    for ch in text.chars() {
        face.load_char(ch as usize, ft::face::RENDER).unwrap();
        let g = face.glyph();

        let bitmap = g.bitmap();
        let texture = Texture::from_memory_alpha(bitmap.buffer(),
                                                 bitmap.width() as u32,
                                                 bitmap.rows() as u32).unwrap();
        Image::colored(color::BLACK).draw(
            &texture,
            default_draw_state(),
            t.trans((x + g.bitmap_left()) as f64, (y - g.bitmap_top()) as f64),
            gl
        );

        x += (g.advance().x >> 6) as i32;
        y += (g.advance().y >> 6) as i32;
    }
}

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Sdl2Window::new(
        opengl,
        WindowSettings {
            title: "Font with Piston".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    let freetype = ft::Library::init().unwrap();
    let font = Path::new("./examples/assets/FiraSans-Regular.ttf");
    let mut face = freetype.new_face(&font, 0).unwrap();
    face.set_pixel_sizes(0, 48).unwrap();

    let ref mut gl = Gl::new(opengl);

    let window = Rc::new(RefCell::new(window));
    for e in piston::events(window) {
        match e {
            Event::Render(args) => {
                gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
                    let transform = c.transform.trans(0.0, 100.0);

                    graphics::clear(color::WHITE, gl);
                    render_text(&mut face, gl, transform, "Hello Piston!");
                });
            },
            _ => ()
        }
    }
}

<commit_msg>Upgrade piston example to latest window, graphics<commit_after>extern crate graphics;
extern crate freetype as ft;
extern crate sdl2_window;
extern crate opengl_graphics;
extern crate piston;

use std::rc::Rc;
use std::cell::RefCell;
use std::path::Path;
use sdl2_window::Sdl2Window;
use opengl_graphics::{ GlGraphics, Texture, OpenGL };
use graphics::math::Matrix2d;
use piston::window::{ WindowSettings, Size };

fn render_text(face: &mut ft::Face, gl: &mut GlGraphics, t: Matrix2d, text: &str) {
    use graphics::*;

    let mut x = 0;
    let mut y = 0;
    for ch in text.chars() {
        face.load_char(ch as usize, ft::face::RENDER).unwrap();
        let g = face.glyph();

        let bitmap = g.bitmap();
        let texture = Texture::from_memory_alpha(bitmap.buffer(),
                                                 bitmap.width() as u32,
                                                 bitmap.rows() as u32).unwrap();
        Image::new_colored(color::BLACK).draw(
            &texture,
            default_draw_state(),
            t.trans((x + g.bitmap_left()) as f64, (y - g.bitmap_top()) as f64),
            gl
        );

        x += (g.advance().x >> 6) as i32;
        y += (g.advance().y >> 6) as i32;
    }
}

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Sdl2Window::new(
        opengl,
        WindowSettings::new("Font with Piston".to_string(),
                            Size { width: 300, height: 300 })
                           .exit_on_esc(true)
    );

    let freetype = ft::Library::init().unwrap();
    let font = Path::new("./examples/assets/FiraSans-Regular.ttf");
    let mut face = freetype.new_face(&font, 0).unwrap();
    face.set_pixel_sizes(0, 48).unwrap();

    let ref mut gl = GlGraphics::new(opengl);

    let window = Rc::new(RefCell::new(window));
    for e in piston::events(window) {
        use piston::event::*;

        if let Some(args) = e.render_args() {
            use graphics::*;

            gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
                let transform = c.transform.trans(0.0, 100.0);

                clear(color::WHITE, gl);
                render_text(&mut face, gl, transform, "Hello Piston!");
            });

        }
    }
}

<|endoftext|>


<commit_before>//! Contains the ThreadPool and the individual Threads.

use std::sync::{Arc,RwLock};
use std::sync::atomic::{AtomicBool,Ordering};
use std::thread::{JoinHandle,self};
use std::sync::mpsc::{channel,Receiver,Sender};

use std::{mem,time};

use pleco::board::*;
use pleco::core::piece_move::BitMove;
use pleco::tools::tt::*;

use super::search::ThreadSearcher;
use super::misc::*;
use super::{TT_TABLE,THREAD_STACK_SIZE};
use super::root_moves::RootMove;
use super::root_moves::root_moves_list::RootMoveList;
use super::root_moves::root_moves_manager::RmManager;
use super::sync::LockLatch;

pub struct ThreadGo {
    limit: Limits,
    board: Board,
    // Options: ?
}

pub enum SendData {
    BestMove(RootMove)
}

pub struct ThreadPool {
    // This is the position information we send to each thread upon
    // starting. Contains stuff like the Board, and the Limit to search
    // to.
    pos_state: Arc<RwLock<Option<ThreadGo>>>,

    // This is all rootmoves for all treads.
    rm_manager: RmManager,

    // Join handle for the main thread.
    main_thread: Option<JoinHandle<()>>,

    // The mainthread will send us information through this! Such as
    // the best move available.
    receiver: Receiver<SendData>,

    // CondVar that the mainthread blocks on. We will notif the main thread
    // to awaken through this.
    main_thread_go: Arc<LockLatch>,

    // Vector of all non-main threads
    threads: Vec<JoinHandle<()>>,

    // Tells all threads to go. This is mostly used by the MainThread, we
    // don't really touch this at all.
    all_thread_go: Arc<LockLatch>,

    // For each thread (including the mainthread), is it finished?

    // Tells all threads to stop and return the ebstmove found
    stop: Arc<AtomicBool>,

    // Tells the threads to drop.
    drop: Arc<AtomicBool>,

    use_stdout: Arc<AtomicBool>,

    stop_guard: Arc<AtomicBool>
}

// Okay, this all looks like madness, but there is some reason to it all.
// Basically, Threadpool manages spawning and despawning threads, as well
// as passing state to / from those threads, telling them to stop, go, drop,
// and lastly determining the "best move" from all the threads.

// While we spawn all the other threads, We mostly communicate with the
// MainThread to do anything useful. We let the mainthread handle anything fun.
// The goal of the ThreadPool is to be NON BLOCKING, unless we want to await a
// result.

impl ThreadPool {
    fn init(rx: Receiver<SendData>) -> Self {
        ThreadPool {
            pos_state: Arc::new(RwLock::new(None)),
            rm_manager: RmManager::new(),
            main_thread: None,
            receiver: rx,
            main_thread_go: Arc::new(LockLatch::new()),
            threads: Vec::with_capacity(8),
            all_thread_go: Arc::new(LockLatch::new()),
            stop: Arc::new(AtomicBool::new(false)),
            drop: Arc::new(AtomicBool::new(false)),
            use_stdout: Arc::new(AtomicBool::new(false)),
            stop_guard: Arc::new(AtomicBool::new(true))
        }
    }

    fn create_thread(&self, id: usize, root_moves: RootMoveList) -> Thread {
        Thread {
            root_moves: root_moves,
            id: id,
            tt: &super::TT_TABLE,
            use_stdout: Arc::clone(&self.use_stdout),
            stop: Arc::clone(&self.stop),
            drop: Arc::clone(&self.drop),
            pos_state: Arc::clone(&self.pos_state),
            cond: Arc::clone(&self.all_thread_go),
            thread_stack: init_thread_stack(),
        }
    }

    fn spawn_main_thread(&mut self, tx: Sender<SendData>) {
        let root_moves = self.rm_manager.add_thread().unwrap();
        let thread = self.create_thread(0, root_moves);
        let main_thread = MainThread {
            per_thread: self.rm_manager.clone(),
            main_thread_go: Arc::clone(&self.main_thread_go),
            sender: tx,
            thread,
            stop_guard: self.stop_guard.clone()
        };

        let builder = thread::Builder::new().name(String::from("0"));
        self.main_thread = Some(
            builder.spawn(move || {
                let mut main_thread = main_thread;
                main_thread.main_idle_loop()
            }).unwrap());
    }

    pub fn new() -> Self {
        let (tx, rx) = channel();
        let mut pool = ThreadPool::init(rx);
        pool.spawn_main_thread(tx);
        pool
    }

    pub fn stdout(&mut self, use_stdout: bool) {
        self.use_stdout.store(use_stdout, Ordering::Relaxed)
    }

    pub fn set_thread_count(&mut self, num: usize) {
        // TODO: Check for overflow
        let curr_num: usize = self.rm_manager.size();

        let mut i: usize = curr_num;
        while i < num {
            let root_moves = self.rm_manager.add_thread().unwrap();
            let thread = self.create_thread(i, root_moves);
            let builder = thread::Builder::new().name(i.to_string());
            self.threads.push(builder.spawn(move || {
                let mut current_thread = thread;
                current_thread.idle_loop()
            }).unwrap());
            i += 1;
        }
    }

    pub fn uci_search(&mut self, board: &Board, limits: &PreLimits) {
        self.stop_guard.store(false, Ordering::SeqCst);
        {
            let mut thread_go = self.pos_state.write().unwrap();
            *thread_go = Some(ThreadGo {
                board: board.shallow_clone(),
                limit: (limits.clone()).create()
            });
        }
        self.main_thread_go.set();
        while !self.stop_guard.load(Ordering::SeqCst) {}
    }

    pub fn search(&mut self, board: &Board, limits: &PreLimits) -> BitMove {
        self.uci_search(&board, &limits);
        let data = self.receiver.recv().unwrap();
        match data {
            SendData::BestMove(t) => t.bit_move
        }
    }

    pub fn get_move(&self) -> BitMove {
        let data = self.receiver.recv().unwrap();
        match data {
            SendData::BestMove(t) => t.bit_move
        }
    }

    pub fn stop_searching(&mut self) {
        self.stop.store(true, Ordering::Relaxed);
    }
}

pub struct MainThread {
    per_thread: RmManager,
    main_thread_go: Arc<LockLatch>,
    sender: Sender<SendData>,
    thread: Thread,
    stop_guard: Arc<AtomicBool>
}

impl MainThread {
    pub fn main_idle_loop(&mut self) {
        while !self.thread.drop() {
            self.main_thread_go.wait();
            if self.thread.drop() {
                return;
            }
            self.go();
        }
    }

    pub fn lock_threads(&mut self) {
        self.thread.cond.lock();
    }

    pub fn start_threads(&mut self) {
        self.thread.cond.set();
    }

    pub fn lock_self(&mut self) {
        self.main_thread_go.lock();
    }

    pub fn go(&mut self) {
        self.thread.root_moves.set_finished(true);
        self.thread.stop.store(true, Ordering::Relaxed);
        self.per_thread.wait_for_finish();
        self.per_thread.reset_depths();
        let board = self.thread.retrieve_board().unwrap();
        unsafe {
            self.per_thread.replace_moves(&board);
        }

        // turn stop searching off
        self.thread.stop.store(false, Ordering::Relaxed);
        // wakeup all threads
        self.start_threads();

        let limit = self.thread.retrieve_limit().unwrap();
        self.thread.root_moves.set_finished(false);
        self.per_thread.wait_for_start();
        self.lock_threads();

        // start searching
        self.stop_guard.store(true, Ordering::SeqCst);
        self.thread.start_searching(board, limit);
        self.thread.root_moves.set_finished(true);
        self.thread.stop.store(true, Ordering::Relaxed);
        self.per_thread.wait_for_finish();

        // find best move
        let best_root_move: RootMove = self.per_thread.best_rootmove(self.thread.use_stdout.load(Ordering::Relaxed));

        self.sender.send(SendData::BestMove(best_root_move)).unwrap();

        if self.thread.use_stdout.load(Ordering::Relaxed) {
            println!("bestmove {}", best_root_move.bit_move);
        }

        // return to idle loop
        self.lock_self();
    }
}

pub struct Thread {
    pub root_moves: RootMoveList,
    pub id: usize,
    pub tt: &'static TranspositionTable,
    pub use_stdout: Arc<AtomicBool>,
    pub stop: Arc<AtomicBool>,
    pub drop: Arc<AtomicBool>,
    pub pos_state: Arc<RwLock<Option<ThreadGo>>>,
    pub cond: Arc<LockLatch>,
    pub thread_stack: [ThreadStack; THREAD_STACK_SIZE],
}

impl Thread {

    pub fn drop(&self) -> bool {
        self.drop.load(Ordering::Relaxed)
    }

    pub fn retrieve_board(&self) -> Option<Board> {
        let s: &Option<ThreadGo> = &*(self.pos_state.read().unwrap());
        let board = s.as_ref().map(|ref tg| (*tg).board.shallow_clone());
        board
    }

    pub fn retrieve_limit(&self) -> Option<Limits> {
        let s: &Option<ThreadGo> = &*(self.pos_state.read().unwrap());
        let board = s.as_ref().map(|ref tg| (*tg).limit.clone());
        board
    }

    pub fn idle_loop(&mut self) {
        self.root_moves.set_finished(true);
        while !self.drop(){
            self.cond.wait();
            if self.drop() {
                return;
            }
            self.root_moves.set_finished(false);
            self.go();
            self.root_moves.set_finished(true);
        }
    }

    fn start_searching(&mut self, board: Board, limit: Limits) {
        let mut thread_search = ThreadSearcher {
            thread: self,
            limit: limit,
            board: board
        };
        thread_search.search_root();
    }

    pub fn go(&mut self) {
        let board = self.retrieve_board().unwrap();
        let limit = self.retrieve_limit().unwrap();
        self.start_searching(board, limit);
    }
}

impl Drop for ThreadPool {
    fn drop(&mut self) {
        // Store that we are dropping
        self.drop.store(true, Ordering::Relaxed);
        thread::sleep(time::Duration::new(0,100));
        self.stop.store(true, Ordering::Relaxed);

        // Notify the main thread to wakeup and stop
        self.main_thread_go.set();

        // Notify the other threads to wakeup and stop
        self.all_thread_go.set();

        // Join all the handles
        while !self.threads.is_empty() {
            let thread_handle = self.threads.pop().unwrap();
            thread_handle.join().unwrap();
        }
        self.main_thread.take().unwrap().join().unwrap();
    }
}

pub fn init_thread_stack() -> [ThreadStack; THREAD_STACK_SIZE] {
    let s: [ThreadStack; THREAD_STACK_SIZE] = unsafe { mem::zeroed() };
    s
}
<commit_msg>Removed the stop guard, causing some issues.<commit_after>//! Contains the ThreadPool and the individual Threads.

use std::sync::{Arc,RwLock};
use std::sync::atomic::{AtomicBool,Ordering};
use std::thread::{JoinHandle,self};
use std::sync::mpsc::{channel,Receiver,Sender};

use std::{mem,time};

use pleco::board::*;
use pleco::core::piece_move::BitMove;
use pleco::tools::tt::*;

use super::search::ThreadSearcher;
use super::misc::*;
use super::{TT_TABLE,THREAD_STACK_SIZE};
use super::root_moves::RootMove;
use super::root_moves::root_moves_list::RootMoveList;
use super::root_moves::root_moves_manager::RmManager;
use super::sync::LockLatch;

pub struct ThreadGo {
    limit: Limits,
    board: Board,
    // Options: ?
}

pub enum SendData {
    BestMove(RootMove)
}

pub struct ThreadPool {
    // This is the position information we send to each thread upon
    // starting. Contains stuff like the Board, and the Limit to search
    // to.
    pos_state: Arc<RwLock<Option<ThreadGo>>>,

    // This is all rootmoves for all treads.
    rm_manager: RmManager,

    // Join handle for the main thread.
    main_thread: Option<JoinHandle<()>>,

    // The mainthread will send us information through this! Such as
    // the best move available.
    receiver: Receiver<SendData>,

    // CondVar that the mainthread blocks on. We will notif the main thread
    // to awaken through this.
    main_thread_go: Arc<LockLatch>,

    // Vector of all non-main threads
    threads: Vec<JoinHandle<()>>,

    // Tells all threads to go. This is mostly used by the MainThread, we
    // don't really touch this at all.
    all_thread_go: Arc<LockLatch>,

    // For each thread (including the mainthread), is it finished?

    // Tells all threads to stop and return the ebstmove found
    stop: Arc<AtomicBool>,

    // Tells the threads to drop.
    drop: Arc<AtomicBool>,

    use_stdout: Arc<AtomicBool>,
}

// Okay, this all looks like madness, but there is some reason to it all.
// Basically, Threadpool manages spawning and despawning threads, as well
// as passing state to / from those threads, telling them to stop, go, drop,
// and lastly determining the "best move" from all the threads.

// While we spawn all the other threads, We mostly communicate with the
// MainThread to do anything useful. We let the mainthread handle anything fun.
// The goal of the ThreadPool is to be NON BLOCKING, unless we want to await a
// result.

impl ThreadPool {
    fn init(rx: Receiver<SendData>) -> Self {
        ThreadPool {
            pos_state: Arc::new(RwLock::new(None)),
            rm_manager: RmManager::new(),
            main_thread: None,
            receiver: rx,
            main_thread_go: Arc::new(LockLatch::new()),
            threads: Vec::with_capacity(8),
            all_thread_go: Arc::new(LockLatch::new()),
            stop: Arc::new(AtomicBool::new(false)),
            drop: Arc::new(AtomicBool::new(false)),
            use_stdout: Arc::new(AtomicBool::new(false)),
        }
    }

    fn create_thread(&self, id: usize, root_moves: RootMoveList) -> Thread {
        Thread {
            root_moves: root_moves,
            id: id,
            tt: &super::TT_TABLE,
            use_stdout: Arc::clone(&self.use_stdout),
            stop: Arc::clone(&self.stop),
            drop: Arc::clone(&self.drop),
            pos_state: Arc::clone(&self.pos_state),
            cond: Arc::clone(&self.all_thread_go),
            thread_stack: init_thread_stack(),
        }
    }

    fn spawn_main_thread(&mut self, tx: Sender<SendData>) {
        let root_moves = self.rm_manager.add_thread().unwrap();
        let thread = self.create_thread(0, root_moves);
        let main_thread = MainThread {
            per_thread: self.rm_manager.clone(),
            main_thread_go: Arc::clone(&self.main_thread_go),
            sender: tx,
            thread
        };

        let builder = thread::Builder::new().name(String::from("0"));
        self.main_thread = Some(
            builder.spawn(move || {
                let mut main_thread = main_thread;
                main_thread.main_idle_loop()
            }).unwrap());
    }

    pub fn new() -> Self {
        let (tx, rx) = channel();
        let mut pool = ThreadPool::init(rx);
        pool.spawn_main_thread(tx);
        pool
    }

    pub fn stdout(&mut self, use_stdout: bool) {
        self.use_stdout.store(use_stdout, Ordering::Relaxed)
    }

    pub fn set_thread_count(&mut self, num: usize) {
        // TODO: Check for overflow
        let curr_num: usize = self.rm_manager.size();

        let mut i: usize = curr_num;
        while i < num {
            let root_moves = self.rm_manager.add_thread().unwrap();
            let thread = self.create_thread(i, root_moves);
            let builder = thread::Builder::new().name(i.to_string());
            self.threads.push(builder.spawn(move || {
                let mut current_thread = thread;
                current_thread.idle_loop()
            }).unwrap());
            i += 1;
        }
    }

    pub fn uci_search(&mut self, board: &Board, limits: &PreLimits) {
        {
            let mut thread_go = self.pos_state.write().unwrap();
            *thread_go = Some(ThreadGo {
                board: board.shallow_clone(),
                limit: (limits.clone()).create()
            });
        }
        self.main_thread_go.set();
    }

    pub fn search(&mut self, board: &Board, limits: &PreLimits) -> BitMove {
        self.uci_search(&board, &limits);
        let data = self.receiver.recv().unwrap();
        match data {
            SendData::BestMove(t) => t.bit_move
        }
    }

    pub fn get_move(&self) -> BitMove {
        let data = self.receiver.recv().unwrap();
        match data {
            SendData::BestMove(t) => t.bit_move
        }
    }

    pub fn stop_searching(&mut self) {
        self.stop.store(true, Ordering::Relaxed);
    }
}

pub struct MainThread {
    per_thread: RmManager,
    main_thread_go: Arc<LockLatch>,
    sender: Sender<SendData>,
    thread: Thread,
}

impl MainThread {
    pub fn main_idle_loop(&mut self) {
        while !self.thread.drop() {
            self.main_thread_go.wait();
            if self.thread.drop() {
                return;
            }
            self.go();
        }
    }

    pub fn lock_threads(&mut self) {
        self.thread.cond.lock();
    }

    pub fn start_threads(&mut self) {
        self.thread.cond.set();
    }

    pub fn lock_self(&mut self) {
        self.main_thread_go.lock();
    }

    pub fn go(&mut self) {
        self.thread.root_moves.set_finished(true);
        self.thread.stop.store(true, Ordering::Relaxed);
        self.per_thread.wait_for_finish();
        self.per_thread.reset_depths();
        let board = self.thread.retrieve_board().unwrap();
        unsafe {
            self.per_thread.replace_moves(&board);
        }

        // turn stop searching off
        self.thread.stop.store(false, Ordering::Relaxed);
        // wakeup all threads
        self.start_threads();

        let limit = self.thread.retrieve_limit().unwrap();
        self.thread.root_moves.set_finished(false);
        self.per_thread.wait_for_start();
        self.lock_threads();

        // start searching
        self.thread.start_searching(board, limit);
        self.thread.root_moves.set_finished(true);
        self.thread.stop.store(true, Ordering::Relaxed);
        self.per_thread.wait_for_finish();

        // find best move
        let best_root_move: RootMove = self.per_thread.best_rootmove(self.thread.use_stdout.load(Ordering::Relaxed));

        self.sender.send(SendData::BestMove(best_root_move)).unwrap();

        if self.thread.use_stdout.load(Ordering::Relaxed) {
            println!("bestmove {}", best_root_move.bit_move);
        }

        // return to idle loop
        self.lock_self();
    }
}

pub struct Thread {
    pub root_moves: RootMoveList,
    pub id: usize,
    pub tt: &'static TranspositionTable,
    pub use_stdout: Arc<AtomicBool>,
    pub stop: Arc<AtomicBool>,
    pub drop: Arc<AtomicBool>,
    pub pos_state: Arc<RwLock<Option<ThreadGo>>>,
    pub cond: Arc<LockLatch>,
    pub thread_stack: [ThreadStack; THREAD_STACK_SIZE],
}

impl Thread {

    pub fn drop(&self) -> bool {
        self.drop.load(Ordering::Relaxed)
    }

    pub fn retrieve_board(&self) -> Option<Board> {
        let s: &Option<ThreadGo> = &*(self.pos_state.read().unwrap());
        let board = s.as_ref().map(|ref tg| (*tg).board.shallow_clone());
        board
    }

    pub fn retrieve_limit(&self) -> Option<Limits> {
        let s: &Option<ThreadGo> = &*(self.pos_state.read().unwrap());
        let board = s.as_ref().map(|ref tg| (*tg).limit.clone());
        board
    }

    pub fn idle_loop(&mut self) {
        self.root_moves.set_finished(true);
        while !self.drop(){
            self.cond.wait();
            if self.drop() {
                return;
            }
            self.root_moves.set_finished(false);
            self.go();
            self.root_moves.set_finished(true);
        }
    }

    fn start_searching(&mut self, board: Board, limit: Limits) {
        let mut thread_search = ThreadSearcher {
            thread: self,
            limit: limit,
            board: board
        };
        thread_search.search_root();
    }

    pub fn go(&mut self) {
        let board = self.retrieve_board().unwrap();
        let limit = self.retrieve_limit().unwrap();
        self.start_searching(board, limit);
    }
}

impl Drop for ThreadPool {
    fn drop(&mut self) {
        // Store that we are dropping
        self.drop.store(true, Ordering::Relaxed);
        thread::sleep(time::Duration::new(0,100));
        self.stop.store(true, Ordering::Relaxed);

        // Notify the main thread to wakeup and stop
        self.main_thread_go.set();

        // Notify the other threads to wakeup and stop
        self.all_thread_go.set();

        // Join all the handles
        while !self.threads.is_empty() {
            let thread_handle = self.threads.pop().unwrap();
            thread_handle.join().unwrap();
        }
        self.main_thread.take().unwrap().join().unwrap();
    }
}

pub fn init_thread_stack() -> [ThreadStack; THREAD_STACK_SIZE] {
    let s: [ThreadStack; THREAD_STACK_SIZE] = unsafe { mem::zeroed() };
    s
}
<|endoftext|>