<commit_before>/// Character classes

use std::char;
use std::cmp::{min, max};

/// A range of codepoints.
pub type Range = (char, char);

/// A character class is a non-empty collection of ranges.
pub enum CharClass {
    priv CCOwned(~[Range]),
    priv CCStatic(&'static [Range])
}

impl CharClass {
    /// Create a class from a vector of ranges.
    pub fn new(mut r: ~[Range]) -> CharClass {
        if r.iter().any(|&(lo, hi)| lo > hi) {
            fail!("invalid range");
        }

        r.sort_by(|a, b| {
            // Sort by start value ascending, end value descending
            match a.lo().cmp(&b.lo()) {
                Equal => b.hi().cmp(&a.hi()),
                o => o
            }
        });

        if r.len() > 0 {
            // Coalesce overlapping ranges in place
            let mut cursor = 0;
            for i in range(1, r.len()) {
                if r[cursor].hi() == char::MAX {
                    break
                } else if r[i].lo() <= next_char(r[cursor].hi()) {
                    // Merge the two ranges
                    r[cursor] = (
                        min(r[cursor].lo(), r[i].lo()),
                        max(r[cursor].hi(), r[i].hi()));
                } else {
                    cursor = i;
                }
            }
            r.truncate(cursor + 1); r.shrink_to_fit();
            CCOwned(r)
        } else {
            fail!("char class cannot be empty")
        }
    }

    /// Create a class matching a single code point.
    pub fn from_char(c: char) -> CharClass {
        CharClass::new(~[(c, c)])
    }

    /// Create a class from a range of code points.
    pub fn from_range(lo: char, hi: char) -> CharClass {
        CharClass::new(~[(lo, hi)])
    }

    /// Combine several classes into one that subsumes them all.
    pub fn combine(classes: &[CharClass]) -> CharClass {
        let mut ranges: ~[Range] = ~[];
        for cc in classes.iter() {
            for r in cc.ranges().iter() {
                ranges.push(*r);
            }
        }
        CharClass::new(ranges)
    }

    ///
    /// Get the list of ranges contained in the character class.
    ///
    /// The returned vector is always sorted, with no overlapping
    /// ranges.
    ///
    pub fn ranges<'a>(&'a self) -> &'a [Range] {
        match *self {
            CCOwned(ref r) => {
                let r_borrow: &'a [Range] = *r;
                r_borrow
            },
            CCStatic(r) => {
                let r_borrow: &'a [Range] = r;
                r_borrow
            }
        }
    }

    /// If the class matches a single code point, return it; otherwise
    /// return `None`.
    pub fn to_char(&self) -> Option<char> {
        match self.ranges() {
            [(lo, hi)] if lo == hi => Some(lo),
            _ => None
        }
    }

    /// Determine if the character is contained in the class.
    pub fn includes(&self, c: char) -> bool {
        self.ranges().bsearch(|&(lo, hi)| {
            if c < lo {
                Greater
            } else if hi < c {
                Less
            } else {
                Equal
            }
        }).is_some()
    }

    /// Return the negation of the character class.
    pub fn negate(&self) -> CharClass {
        let ranges = self.ranges();
        let mut result: ~[Range] = ~[];

        let first_lo = ranges[0].lo();
        if first_lo != '\0' {
            result.push(('\0', prev_char(first_lo)));
        }

        let mut last_hi = ranges[0].hi();
        for &(lo, hi) in ranges.slice_from(1).iter() {
            result.push((next_char(last_hi), prev_char(lo)));
            last_hi = hi;
        }

        if last_hi != char::MAX {
            result.push((next_char(last_hi), char::MAX));
        }

        CCOwned(result)
    }
}

trait RangeUtils {
    fn lo(&self) -> char;
    fn hi(&self) -> char;
}

impl RangeUtils for (char, char) {
    #[inline]
    fn lo(&self) -> char {
        let &(lo, _) = self;
        lo
    }

    #[inline]
    fn hi(&self) -> char {
        let &(_, hi) = self;
        hi
    }
}

#[inline]
fn prev_char(c: char) -> char {
    char::from_u32(c as u32 - 1).unwrap()
}

#[inline]
fn next_char(c: char) -> char {
    char::from_u32(c as u32 + 1).unwrap()
}

#[cfg(test)]
mod test {
    use std::char;
    use super::CharClass;

    #[test]
    #[should_fail]
    fn invalid_range() {
        let _ = CharClass::new(~[('b', 'a')]);
    }

    #[test]
    #[should_fail]
    fn empty_class() {
        let _ = CharClass::new(~[]);
    }

    #[test]
    fn class_sorted() {
        let c = CharClass::new(~[('y', 'z'), ('a', 'b')]);
        assert_eq!(c.ranges(), [('a', 'b'), ('y', 'z')]);
    }

    #[test]
    fn class_optimize() {
        let c1 = CharClass::new(~[('a', 'b'), ('c', 'd')]);  // touching
        let c2 = CharClass::new(~[('a', 'c'), ('b', 'd')]);  // overlapping
        let c3 = CharClass::new(~[('a', 'd'), ('b', 'c')]);  // contained
        assert_eq!(c1.ranges(), [('a', 'd')]);
        assert_eq!(c2.ranges(), [('a', 'd')]);
        assert_eq!(c3.ranges(), [('a', 'd')]);
    }

    #[test]
    fn class_includes() {
        // This will fail if the bsearch is incorrect
        let c = CharClass::new(~[('a', 'b'), ('i', 'j'), ('y', 'z')]);
        assert!(c.includes('b'));
        assert!(c.includes('y'));
    }

    #[test]
    fn negate_simple() {
        let c = CharClass::new(~[('T', 's'), ('☻', '♪')]).negate();
        assert_eq!(c.ranges(), [('\0', 'S'), ('t', '☺'), ('♫', char::MAX)]);
    }

    #[test]
    fn negate_edge_case() {
        let c = CharClass::new(~[('\0', char::MAX)]).negate();
        assert_eq!(c.ranges(), []);
    }
}

pub mod ascii;
<commit_msg>Fix bug in char class constructor<commit_after>/// Character classes

use std::char;
use std::cmp::{min, max};

/// A range of codepoints.
pub type Range = (char, char);

/// A character class is a non-empty collection of ranges.
pub enum CharClass {
    priv CCOwned(~[Range]),
    priv CCStatic(&'static [Range])
}

impl CharClass {
    /// Create a class from a vector of ranges.
    pub fn new(mut r: ~[Range]) -> CharClass {
        if r.iter().any(|&(lo, hi)| lo > hi) {
            fail!("invalid range");
        }

        r.sort_by(|a, b| {
            // Sort by start value ascending, end value descending
            match a.lo().cmp(&b.lo()) {
                Equal => b.hi().cmp(&a.hi()),
                o => o
            }
        });

        if r.len() > 0 {
            // Coalesce overlapping ranges in place
            let mut cursor = 0;
            for i in range(1, r.len()) {
                if r[cursor].hi() == char::MAX {
                    break
                } else if r[i].lo() <= next_char(r[cursor].hi()) {
                    // Merge the two ranges
                    r[cursor] = (
                        min(r[cursor].lo(), r[i].lo()),
                        max(r[cursor].hi(), r[i].hi()));
                } else {
                    // Create a new range
                    cursor += 1;
                    r[cursor] = r[i];
                }
            }
            r.truncate(cursor + 1); r.shrink_to_fit();
            CCOwned(r)
        } else {
            fail!("char class cannot be empty")
        }
    }

    /// Create a class matching a single code point.
    pub fn from_char(c: char) -> CharClass {
        CharClass::new(~[(c, c)])
    }

    /// Create a class from a range of code points.
    pub fn from_range(lo: char, hi: char) -> CharClass {
        CharClass::new(~[(lo, hi)])
    }

    /// Combine several classes into one that subsumes them all.
    pub fn combine(classes: &[CharClass]) -> CharClass {
        let mut ranges: ~[Range] = ~[];
        for cc in classes.iter() {
            for r in cc.ranges().iter() {
                ranges.push(*r);
            }
        }
        CharClass::new(ranges)
    }

    ///
    /// Get the list of ranges contained in the character class.
    ///
    /// The returned vector is always sorted, with no overlapping
    /// ranges.
    ///
    pub fn ranges<'a>(&'a self) -> &'a [Range] {
        match *self {
            CCOwned(ref r) => {
                let r_borrow: &'a [Range] = *r;
                r_borrow
            },
            CCStatic(r) => {
                let r_borrow: &'a [Range] = r;
                r_borrow
            }
        }
    }

    /// If the class matches a single code point, return it; otherwise
    /// return `None`.
    pub fn to_char(&self) -> Option<char> {
        match self.ranges() {
            [(lo, hi)] if lo == hi => Some(lo),
            _ => None
        }
    }

    /// Determine if the character is contained in the class.
    pub fn includes(&self, c: char) -> bool {
        self.ranges().bsearch(|&(lo, hi)| {
            if c < lo {
                Greater
            } else if hi < c {
                Less
            } else {
                Equal
            }
        }).is_some()
    }

    /// Return the negation of the character class.
    pub fn negate(&self) -> CharClass {
        let ranges = self.ranges();
        let mut result: ~[Range] = ~[];

        let first_lo = ranges[0].lo();
        if first_lo != '\0' {
            result.push(('\0', prev_char(first_lo)));
        }

        let mut last_hi = ranges[0].hi();
        for &(lo, hi) in ranges.slice_from(1).iter() {
            result.push((next_char(last_hi), prev_char(lo)));
            last_hi = hi;
        }

        if last_hi != char::MAX {
            result.push((next_char(last_hi), char::MAX));
        }

        CCOwned(result)
    }
}

trait RangeUtils {
    fn lo(&self) -> char;
    fn hi(&self) -> char;
}

impl RangeUtils for (char, char) {
    #[inline]
    fn lo(&self) -> char {
        let &(lo, _) = self;
        lo
    }

    #[inline]
    fn hi(&self) -> char {
        let &(_, hi) = self;
        hi
    }
}

#[inline]
fn prev_char(c: char) -> char {
    char::from_u32(c as u32 - 1).unwrap()
}

#[inline]
fn next_char(c: char) -> char {
    char::from_u32(c as u32 + 1).unwrap()
}

#[cfg(test)]
mod test {
    use std::char;
    use super::CharClass;

    #[test]
    #[should_fail]
    fn invalid_range() {
        let _ = CharClass::new(~[('b', 'a')]);
    }

    #[test]
    #[should_fail]
    fn empty_class() {
        let _ = CharClass::new(~[]);
    }

    #[test]
    fn class_sorted() {
        let c = CharClass::new(~[('y', 'z'), ('a', 'b')]);
        assert_eq!(c.ranges(), [('a', 'b'), ('y', 'z')]);
    }

    #[test]
    fn class_optimize() {
        let c1 = CharClass::new(~[('a', 'b'), ('c', 'd')]);  // touching
        let c2 = CharClass::new(~[('a', 'c'), ('b', 'd')]);  // overlapping
        let c3 = CharClass::new(~[('a', 'd'), ('b', 'c')]);  // contained
        assert_eq!(c1.ranges(), [('a', 'd')]);
        assert_eq!(c2.ranges(), [('a', 'd')]);
        assert_eq!(c3.ranges(), [('a', 'd')]);
    }

    #[test]
    fn class_includes() {
        // This will fail if the bsearch is incorrect
        let c = CharClass::new(~[('a', 'b'), ('i', 'j'), ('y', 'z')]);
        assert!(c.includes('b'));
        assert!(c.includes('y'));
    }

    #[test]
    fn negate_simple() {
        let c = CharClass::new(~[('T', 's'), ('☻', '♪')]).negate();
        assert_eq!(c.ranges(), [('\0', 'S'), ('t', '☺'), ('♫', char::MAX)]);
    }

    #[test]
    fn negate_edge_case() {
        let c = CharClass::new(~[('\0', char::MAX)]).negate();
        assert_eq!(c.ranges(), []);
    }

    #[test]
    fn issue_1() {
        // See <https://github.com/lfairy/rose/issues/1>
        let c = CharClass::new(~[('c', 'c'), ('d', 'd'), ('z', 'z')]);
        assert_eq!(c.ranges(), [('c', 'd'), ('z', 'z')]);
    }
}

pub mod ascii;
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Implements parallel traversal over the DOM tree.
//!
//! This traversal is based on Rayon, and therefore its safety is largely
//! verified by the type system.
//!
//! The primary trickiness and fine print for the above relates to the
//! thread safety of the DOM nodes themselves. Accessing a DOM element
//! concurrently on multiple threads is actually mostly "safe", since all
//! the mutable state is protected by an AtomicRefCell, and so we'll
//! generally panic if something goes wrong. Still, we try to to enforce our
//! thread invariants at compile time whenever possible. As such, TNode and
//! TElement are not Send, so ordinary style system code cannot accidentally
//! share them with other threads. In the parallel traversal, we explicitly
//! invoke |unsafe { SendNode::new(n) }| to put nodes in containers that may
//! be sent to other threads. This occurs in only a handful of places and is
//! easy to grep for. At the time of this writing, there is no other unsafe
//! code in the parallel traversal.

#![deny(missing_docs)]

use context::TraversalStatistics;
use dom::{OpaqueNode, SendNode, TElement, TNode};
use rayon;
use scoped_tls::ScopedTLS;
use smallvec::SmallVec;
use std::borrow::Borrow;
use std::mem;
use time;
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};

/// The maximum number of child nodes that we will process as a single unit.
///
/// Larger values will increase style sharing cache hits and general DOM locality
/// at the expense of decreased opportunities for parallelism. This value has not
/// been measured and could potentially be tuned.
pub const WORK_UNIT_MAX: usize = 16;

/// A list of node pointers.
///
/// Note that the inline storage doesn't need to be sized to WORK_UNIT_MAX, but
/// it generally seems sensible to do so.
type NodeList<N> = SmallVec<[SendNode<N>; WORK_UNIT_MAX]>;

/// Entry point for the parallel traversal.
#[allow(unsafe_code)]
pub fn traverse_dom<E, D>(traversal: &D,
                          root: E,
                          token: PreTraverseToken,
                          queue: &rayon::ThreadPool)
    where E: TElement,
          D: DomTraversal<E>,
{
    let dump_stats = traversal.shared_context().options.dump_style_statistics;
    let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
    let mut nodes = NodeList::<E::ConcreteNode>::new();

    debug_assert!(traversal.is_parallel());
    // Handle Gecko's eager initial styling. We don't currently support it
    // in conjunction with bottom-up traversal. If we did, we'd need to put
    // it on the context to make it available to the bottom-up phase.
    let depth = if token.traverse_unstyled_children_only() {
        debug_assert!(!D::needs_postorder_traversal());
        for kid in root.as_node().traversal_children() {
            if kid.as_element().map_or(false, |el| el.get_data().is_none()) {
                nodes.push(unsafe { SendNode::new(kid) });
            }
        }
        root.depth() + 1
    } else {
        nodes.push(unsafe { SendNode::new(root.as_node()) });
        root.depth()
    };

    if nodes.is_empty() {
        return;
    }

    let traversal_data = PerLevelTraversalData {
        current_dom_depth: depth,
    };
    let tls = ScopedTLS::<D::ThreadLocalContext>::new(queue);
    let root = root.as_node().opaque();

    queue.install(|| {
        rayon::scope(|scope| {
            traverse_nodes(nodes,
                           DispatchMode::TailCall,
                           root,
                           traversal_data,
                           scope,
                           traversal,
                           &tls);
        });
    });

    // Dump statistics to stdout if requested.
    if dump_stats {
        let slots = unsafe { tls.unsafe_get() };
        let mut aggregate = slots.iter().fold(TraversalStatistics::default(), |acc, t| {
            match *t.borrow() {
                None => acc,
                Some(ref cx) => &cx.borrow().statistics + &acc,
            }
        });
        aggregate.finish(traversal, start_time.unwrap());
        if aggregate.is_large_traversal() {
            println!("{}", aggregate);
        }
    }
}

/// A callback to create our thread local context.  This needs to be
/// out of line so we don't allocate stack space for the entire struct
/// in the caller.
#[inline(never)]
fn create_thread_local_context<'scope, E, D>(
    traversal: &'scope D,
    slot: &mut Option<D::ThreadLocalContext>)
    where E: TElement + 'scope,
          D: DomTraversal<E>
{
    *slot = Some(traversal.create_thread_local_context())
}

/// A parallel top-down DOM traversal.
///
/// This algorithm traverses the DOM in a breadth-first, top-down manner. The
/// goals are:
/// * Never process a child before its parent (since child style depends on
///   parent style). If this were to happen, the styling algorithm would panic.
/// * Prioritize discovering nodes as quickly as possible to maximize
///   opportunities for parallelism.
/// * Style all the children of a given node (i.e. all sibling nodes) on
///   a single thread (with an upper bound to handle nodes with an
///   abnormally large number of children). This is important because we use
///   a thread-local cache to share styles between siblings.
#[inline(always)]
#[allow(unsafe_code)]
fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
                                  root: OpaqueNode,
                                  mut traversal_data: PerLevelTraversalData,
                                  scope: &'a rayon::Scope<'scope>,
                                  traversal: &'scope D,
                                  tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
    where E: TElement + 'scope,
          D: DomTraversal<E>,
{
    debug_assert!(nodes.len() <= WORK_UNIT_MAX);
    let mut discovered_child_nodes = NodeList::<E::ConcreteNode>::new();
    {
        // Scope the borrow of the TLS so that the borrow is dropped before
        // a potential recursive call when we pass TailCall.
        let mut tlc = tls.ensure(
            |slot: &mut Option<D::ThreadLocalContext>| create_thread_local_context(traversal, slot));

        for n in nodes {
            // If the last node we processed produced children, spawn them off
            // into a work item. We do this at the beginning of the loop (rather
            // than at the end) so that we can traverse the children of the last
            // sibling directly on this thread without a spawn call.
            //
            // This has the important effect of removing the allocation and
            // context-switching overhead of the parallel traversal for perfectly
            // linear regions of the DOM, i.e.:
            //
            // <russian><doll><tag><nesting></nesting></tag></doll></russian>
            //
            // Which are not at all uncommon.
            if !discovered_child_nodes.is_empty() {
                let children = mem::replace(&mut discovered_child_nodes, Default::default());
                let mut traversal_data_copy = traversal_data.clone();
                traversal_data_copy.current_dom_depth += 1;
                traverse_nodes(children,
                               DispatchMode::NotTailCall,
                               root,
                               traversal_data_copy,
                               scope,
                               traversal,
                               tls);
            }

            let node = **n;
            let mut children_to_process = 0isize;
            traversal.process_preorder(&traversal_data, &mut *tlc, node);
            if let Some(el) = node.as_element() {
                traversal.traverse_children(&mut *tlc, el, |_tlc, kid| {
                    children_to_process += 1;
                    discovered_child_nodes.push(unsafe { SendNode::new(kid) })
                });
            }

            traversal.handle_postorder_traversal(&mut *tlc, root, node,
                                                 children_to_process);
        }
    }

    // Handle the children of the last element in this work unit. If any exist,
    // we can process them (or at least one work unit's worth of them) directly
    // on this thread by passing TailCall.
    if !discovered_child_nodes.is_empty() {
        traversal_data.current_dom_depth += 1;
        traverse_nodes(discovered_child_nodes,
                       DispatchMode::TailCall,
                       root,
                       traversal_data,
                       scope,
                       traversal,
                       tls);
    }
}

/// Controls whether traverse_nodes may make a recursive call to continue
/// doing work, or whether it should always dispatch work asynchronously.
#[derive(Clone, Copy, PartialEq)]
enum DispatchMode {
    TailCall,
    NotTailCall,
}

impl DispatchMode {
    fn is_tail_call(&self) -> bool { matches!(*self, DispatchMode::TailCall) }
}

#[inline]
fn traverse_nodes<'a, 'scope, E, D>(nodes: NodeList<E::ConcreteNode>,
                                    mode: DispatchMode,
                                    root: OpaqueNode,
                                    traversal_data: PerLevelTraversalData,
                                    scope: &'a rayon::Scope<'scope>,
                                    traversal: &'scope D,
                                    tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
    where E: TElement + 'scope,
          D: DomTraversal<E>,
{
    debug_assert!(!nodes.is_empty());

    // In the common case, our children fit within a single work unit, in which
    // case we can pass the SmallVec directly and avoid extra allocation.
    if nodes.len() <= WORK_UNIT_MAX {
        if mode.is_tail_call() {
            // If this is a tail call, bypass rayon and invoke top_down_dom directly.
            top_down_dom(&nodes, root, traversal_data, scope, traversal, tls);
        } else {
            // The caller isn't done yet. Append to the queue and return synchronously.
            scope.spawn(move |scope| {
                let nodes = nodes;
                top_down_dom(&nodes, root, traversal_data, scope, traversal, tls);
            });
        }
    } else {
        // FIXME(bholley): This should be an ArrayVec.
        let mut first_chunk: Option<NodeList<E::ConcreteNode>> = None;
        for chunk in nodes.chunks(WORK_UNIT_MAX) {
            if mode.is_tail_call() && first_chunk.is_none() {
                first_chunk = Some(chunk.iter().cloned().collect::<NodeList<E::ConcreteNode>>());
            } else {
                let boxed = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
                let traversal_data_copy = traversal_data.clone();
                scope.spawn(move |scope| {
                    let b = boxed;
                    top_down_dom(&*b, root, traversal_data_copy, scope, traversal, tls)
                });

            }
        }

        // If this is a tail call, bypass rayon for the first chunk and invoke top_down_dom
        // directly.
        debug_assert_eq!(first_chunk.is_some(), mode.is_tail_call());
        if let Some(c) = first_chunk {
            debug_assert_eq!(c.len(), WORK_UNIT_MAX);
            top_down_dom(&*c, root, traversal_data, scope, traversal, tls);
        }
    }
}
<commit_msg>Don't do recursive tail calls if there's work in the queue.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Implements parallel traversal over the DOM tree.
//!
//! This traversal is based on Rayon, and therefore its safety is largely
//! verified by the type system.
//!
//! The primary trickiness and fine print for the above relates to the
//! thread safety of the DOM nodes themselves. Accessing a DOM element
//! concurrently on multiple threads is actually mostly "safe", since all
//! the mutable state is protected by an AtomicRefCell, and so we'll
//! generally panic if something goes wrong. Still, we try to to enforce our
//! thread invariants at compile time whenever possible. As such, TNode and
//! TElement are not Send, so ordinary style system code cannot accidentally
//! share them with other threads. In the parallel traversal, we explicitly
//! invoke |unsafe { SendNode::new(n) }| to put nodes in containers that may
//! be sent to other threads. This occurs in only a handful of places and is
//! easy to grep for. At the time of this writing, there is no other unsafe
//! code in the parallel traversal.

#![deny(missing_docs)]

use context::TraversalStatistics;
use dom::{OpaqueNode, SendNode, TElement, TNode};
use rayon;
use scoped_tls::ScopedTLS;
use smallvec::SmallVec;
use std::borrow::Borrow;
use std::mem;
use time;
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};

/// The maximum number of child nodes that we will process as a single unit.
///
/// Larger values will increase style sharing cache hits and general DOM locality
/// at the expense of decreased opportunities for parallelism. This value has not
/// been measured and could potentially be tuned.
pub const WORK_UNIT_MAX: usize = 16;

/// A list of node pointers.
///
/// Note that the inline storage doesn't need to be sized to WORK_UNIT_MAX, but
/// it generally seems sensible to do so.
type NodeList<N> = SmallVec<[SendNode<N>; WORK_UNIT_MAX]>;

/// Entry point for the parallel traversal.
#[allow(unsafe_code)]
pub fn traverse_dom<E, D>(traversal: &D,
                          root: E,
                          token: PreTraverseToken,
                          pool: &rayon::ThreadPool)
    where E: TElement,
          D: DomTraversal<E>,
{
    let dump_stats = traversal.shared_context().options.dump_style_statistics;
    let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
    let mut nodes = NodeList::<E::ConcreteNode>::new();

    debug_assert!(traversal.is_parallel());
    // Handle Gecko's eager initial styling. We don't currently support it
    // in conjunction with bottom-up traversal. If we did, we'd need to put
    // it on the context to make it available to the bottom-up phase.
    let depth = if token.traverse_unstyled_children_only() {
        debug_assert!(!D::needs_postorder_traversal());
        for kid in root.as_node().traversal_children() {
            if kid.as_element().map_or(false, |el| el.get_data().is_none()) {
                nodes.push(unsafe { SendNode::new(kid) });
            }
        }
        root.depth() + 1
    } else {
        nodes.push(unsafe { SendNode::new(root.as_node()) });
        root.depth()
    };

    if nodes.is_empty() {
        return;
    }

    let traversal_data = PerLevelTraversalData {
        current_dom_depth: depth,
    };
    let tls = ScopedTLS::<D::ThreadLocalContext>::new(pool);
    let root = root.as_node().opaque();

    pool.install(|| {
        rayon::scope(|scope| {
            traverse_nodes(nodes,
                           DispatchMode::TailCall,
                           root,
                           traversal_data,
                           scope,
                           pool,
                           traversal,
                           &tls);
        });
    });

    // Dump statistics to stdout if requested.
    if dump_stats {
        let slots = unsafe { tls.unsafe_get() };
        let mut aggregate = slots.iter().fold(TraversalStatistics::default(), |acc, t| {
            match *t.borrow() {
                None => acc,
                Some(ref cx) => &cx.borrow().statistics + &acc,
            }
        });
        aggregate.finish(traversal, start_time.unwrap());
        if aggregate.is_large_traversal() {
            println!("{}", aggregate);
        }
    }
}

/// A callback to create our thread local context.  This needs to be
/// out of line so we don't allocate stack space for the entire struct
/// in the caller.
#[inline(never)]
fn create_thread_local_context<'scope, E, D>(
    traversal: &'scope D,
    slot: &mut Option<D::ThreadLocalContext>)
    where E: TElement + 'scope,
          D: DomTraversal<E>
{
    *slot = Some(traversal.create_thread_local_context())
}

/// A parallel top-down DOM traversal.
///
/// This algorithm traverses the DOM in a breadth-first, top-down manner. The
/// goals are:
/// * Never process a child before its parent (since child style depends on
///   parent style). If this were to happen, the styling algorithm would panic.
/// * Prioritize discovering nodes as quickly as possible to maximize
///   opportunities for parallelism.
/// * Style all the children of a given node (i.e. all sibling nodes) on
///   a single thread (with an upper bound to handle nodes with an
///   abnormally large number of children). This is important because we use
///   a thread-local cache to share styles between siblings.
#[inline(always)]
#[allow(unsafe_code)]
fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
                                  root: OpaqueNode,
                                  mut traversal_data: PerLevelTraversalData,
                                  scope: &'a rayon::Scope<'scope>,
                                  pool: &'scope rayon::ThreadPool,
                                  traversal: &'scope D,
                                  tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
    where E: TElement + 'scope,
          D: DomTraversal<E>,
{
    debug_assert!(nodes.len() <= WORK_UNIT_MAX);
    let mut discovered_child_nodes = NodeList::<E::ConcreteNode>::new();
    {
        // Scope the borrow of the TLS so that the borrow is dropped before
        // a potential recursive call when we pass TailCall.
        let mut tlc = tls.ensure(
            |slot: &mut Option<D::ThreadLocalContext>| create_thread_local_context(traversal, slot));

        for n in nodes {
            // If the last node we processed produced children, spawn them off
            // into a work item. We do this at the beginning of the loop (rather
            // than at the end) so that we can traverse the children of the last
            // sibling directly on this thread without a spawn call.
            //
            // This has the important effect of removing the allocation and
            // context-switching overhead of the parallel traversal for perfectly
            // linear regions of the DOM, i.e.:
            //
            // <russian><doll><tag><nesting></nesting></tag></doll></russian>
            //
            // Which are not at all uncommon.
            if !discovered_child_nodes.is_empty() {
                let children = mem::replace(&mut discovered_child_nodes, Default::default());
                let mut traversal_data_copy = traversal_data.clone();
                traversal_data_copy.current_dom_depth += 1;
                traverse_nodes(children,
                               DispatchMode::NotTailCall,
                               root,
                               traversal_data_copy,
                               scope,
                               pool,
                               traversal,
                               tls);
            }

            let node = **n;
            let mut children_to_process = 0isize;
            traversal.process_preorder(&traversal_data, &mut *tlc, node);
            if let Some(el) = node.as_element() {
                traversal.traverse_children(&mut *tlc, el, |_tlc, kid| {
                    children_to_process += 1;
                    discovered_child_nodes.push(unsafe { SendNode::new(kid) })
                });
            }

            traversal.handle_postorder_traversal(&mut *tlc, root, node,
                                                 children_to_process);
        }
    }

    // Handle the children of the last element in this work unit. If any exist,
    // we can process them (or at least one work unit's worth of them) directly
    // on this thread by passing TailCall.
    if !discovered_child_nodes.is_empty() {
        traversal_data.current_dom_depth += 1;
        traverse_nodes(discovered_child_nodes,
                       DispatchMode::TailCall,
                       root,
                       traversal_data,
                       scope,
                       pool,
                       traversal,
                       tls);
    }
}

/// Controls whether traverse_nodes may make a recursive call to continue
/// doing work, or whether it should always dispatch work asynchronously.
#[derive(Clone, Copy, PartialEq)]
enum DispatchMode {
    TailCall,
    NotTailCall,
}

impl DispatchMode {
    fn is_tail_call(&self) -> bool { matches!(*self, DispatchMode::TailCall) }
}

#[inline]
fn traverse_nodes<'a, 'scope, E, D>(nodes: NodeList<E::ConcreteNode>,
                                    mode: DispatchMode,
                                    root: OpaqueNode,
                                    traversal_data: PerLevelTraversalData,
                                    scope: &'a rayon::Scope<'scope>,
                                    pool: &'scope rayon::ThreadPool,
                                    traversal: &'scope D,
                                    tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
    where E: TElement + 'scope,
          D: DomTraversal<E>,
{
    debug_assert!(!nodes.is_empty());

    // This is a tail call from the perspective of the caller. However, we only
    // want to actually dispatch the job as a tail call if there's nothing left
    // in our local queue. Otherwise we need to return to it to maintain proper
    // breadth-first ordering.
    let may_dispatch_tail = mode.is_tail_call() &&
        !pool.current_thread_has_pending_tasks().unwrap();

    // In the common case, our children fit within a single work unit, in which
    // case we can pass the SmallVec directly and avoid extra allocation.
    if nodes.len() <= WORK_UNIT_MAX {
        if may_dispatch_tail {
            top_down_dom(&nodes, root, traversal_data, scope, pool, traversal, tls);
        } else {
            scope.spawn(move |scope| {
                let nodes = nodes;
                top_down_dom(&nodes, root, traversal_data, scope, pool, traversal, tls);
            });
        }
    } else {
        for chunk in nodes.chunks(WORK_UNIT_MAX) {
            let boxed = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
            let traversal_data_copy = traversal_data.clone();
            scope.spawn(move |scope| {
                let b = boxed;
                top_down_dom(&*b, root, traversal_data_copy, scope, pool, traversal, tls)
            });
        }
    }
}
<|endoftext|>


<commit_before>/**

An implementation of the Graph500 Bread First Search problem in Rust.

*/

use std;
import std::time;
import std::map;
import std::map::hashmap;
import std::deque;
import std::deque::t;
import io::writer_util;
import vec::extensions;
import comm::*;

type node_id = i64;
type graph = [map::set<node_id>];

iface queue<T: send> {
    fn add_back(T);
    fn pop_front() -> T;
    fn size() -> uint;
}

#[doc="Creates a queue based on ports and channels.

This is admittedly not ideal, but it will help us work around the deque
bugs for the time being."]
fn create_queue<T: send>() -> queue<T> {
    type repr<T: send> = {
        p : port<T>,
        c : chan<T>,
        mut s : uint,
    };

    let p = port();
    let c = chan(p);

    impl<T: send> of queue<T> for repr<T> {
        fn add_back(x : T) {
            let x = x;
            send(self.c, x);
            self.s += 1u;
        }

        fn pop_front() -> T {
            self.s -= 1u;
            recv(self.p)
        }

        fn size() -> uint { self.s }
    }

    let Q : repr<T> = { p : p, c : c, mut s : 0u };
    Q as queue::<T>
}

fn make_edges(scale: uint, edgefactor: uint) -> [(node_id, node_id)] {
    let r = rand::rng();

    fn choose_edge(i: node_id, j: node_id, scale: uint, r: rand::rng)
        -> (node_id, node_id) {

        let A = 0.57;
        let B = 0.19;
        let C = 0.19;

        if scale == 0u {
            (i, j)
        }
        else {
            let i = i * 2;
            let j = j * 2;
            let scale = scale - 1u;

            let x = r.next_float();

            if x < A {
                choose_edge(i, j, scale, r)
            }
            else {
                let x = x - A;
                if x < B {
                    choose_edge(i + 1, j, scale, r)
                }
                else {
                    let x = x - B;
                    if x < C {
                        choose_edge(i, j + 1, scale, r)
                    }
                    else {
                        choose_edge(i + 1, j + 1, scale, r)
                    }
                }
            }
        }
    }

    vec::from_fn((1u << scale) * edgefactor) {|_i|
        choose_edge(0, 0, scale, r)
    }
}

fn make_graph(N: uint, edges: [(node_id, node_id)]) -> graph {
    let graph = vec::from_fn(N) {|_i| map::int_hash() };

    vec::each(edges) {|e|
        let (i, j) = e;
        map::set_add(graph[i], j);
        map::set_add(graph[j], i);
        true
    }

    graph
}

#[doc="Returns a vector of all the parents in the BFS tree rooted at key.

Nodes that are unreachable have a parent of -1."]
fn bfs(graph: graph, key: node_id) -> [node_id] {
    let marks : [mut node_id]
        = vec::to_mut(vec::from_elem(vec::len(graph), -1));

    let Q = create_queue();

    Q.add_back(key);
    marks[key] = key;

    while Q.size() > 0u {
        let t = Q.pop_front();

        graph[t].each_key() {|k|
            if marks[k] == -1 {
                marks[k] = t;
                Q.add_back(k);
            }
            true
        };
    }

    vec::from_mut(marks)
}

fn main() {
    let scale = 14u;

    let start = time::precise_time_s();
    let edges = make_edges(scale, 16u);
    let stop = time::precise_time_s();

    io::stdout().write_line(#fmt("Generated %? edges in %? seconds.",
                                 vec::len(edges), stop - start));

    let start = time::precise_time_s();
    let graph = make_graph(1u << scale, edges);
    let stop = time::precise_time_s();

    let mut total_edges = 0u;
    vec::each(graph) {|edges| total_edges += edges.size(); true };

    io::stdout().write_line(#fmt("Generated graph with %? edges in %? seconds.",
                                 total_edges / 2u,
                                 stop - start));

    let start = time::precise_time_s();
    let bfs_tree = bfs(graph, 0);
    let stop = time::precise_time_s();

    io::stdout().write_line(#fmt("BFS completed in %? seconds.",
                                 stop - start));
}<commit_msg>Added most of the Graph500 validation routine. It's very slow.<commit_after>/**

An implementation of the Graph500 Bread First Search problem in Rust.

*/

use std;
import std::time;
import std::map;
import std::map::hashmap;
import std::deque;
import std::deque::t;
import io::writer_util;
import comm::*;
import int::abs;

type node_id = i64;
type graph = [map::set<node_id>];
type bfs_result = [node_id];

iface queue<T: send> {
    fn add_back(T);
    fn pop_front() -> T;
    fn size() -> uint;
}

#[doc="Creates a queue based on ports and channels.

This is admittedly not ideal, but it will help us work around the deque
bugs for the time being."]
fn create_queue<T: send>() -> queue<T> {
    type repr<T: send> = {
        p : port<T>,
        c : chan<T>,
        mut s : uint,
    };

    let p = port();
    let c = chan(p);

    impl<T: send> of queue<T> for repr<T> {
        fn add_back(x : T) {
            let x = x;
            send(self.c, x);
            self.s += 1u;
        }

        fn pop_front() -> T {
            self.s -= 1u;
            recv(self.p)
        }

        fn size() -> uint { self.s }
    }

    let Q : repr<T> = { p : p, c : c, mut s : 0u };
    Q as queue::<T>
}

fn make_edges(scale: uint, edgefactor: uint) -> [(node_id, node_id)] {
    let r = rand::rng();

    fn choose_edge(i: node_id, j: node_id, scale: uint, r: rand::rng)
        -> (node_id, node_id) {

        let A = 0.57;
        let B = 0.19;
        let C = 0.19;

        if scale == 0u {
            (i, j)
        }
        else {
            let i = i * 2;
            let j = j * 2;
            let scale = scale - 1u;

            let x = r.next_float();

            if x < A {
                choose_edge(i, j, scale, r)
            }
            else {
                let x = x - A;
                if x < B {
                    choose_edge(i + 1, j, scale, r)
                }
                else {
                    let x = x - B;
                    if x < C {
                        choose_edge(i, j + 1, scale, r)
                    }
                    else {
                        choose_edge(i + 1, j + 1, scale, r)
                    }
                }
            }
        }
    }

    vec::from_fn((1u << scale) * edgefactor) {|_i|
        choose_edge(0, 0, scale, r)
    }
}

fn make_graph(N: uint, edges: [(node_id, node_id)]) -> graph {
    let graph = vec::from_fn(N) {|_i| map::int_hash() };

    vec::each(edges) {|e|
        let (i, j) = e;
        map::set_add(graph[i], j);
        map::set_add(graph[j], i);
        true
    }

    graph
}

#[doc="Returns a vector of all the parents in the BFS tree rooted at key.

Nodes that are unreachable have a parent of -1."]
fn bfs(graph: graph, key: node_id) -> bfs_result {
    let marks : [mut node_id]
        = vec::to_mut(vec::from_elem(vec::len(graph), -1));

    let Q = create_queue();

    Q.add_back(key);
    marks[key] = key;

    while Q.size() > 0u {
        let t = Q.pop_front();

        graph[t].each_key() {|k|
            if marks[k] == -1 {
                marks[k] = t;
                Q.add_back(k);
            }
            true
        };
    }

    vec::from_mut(marks)
}

#[doc="Performs at least some of the validation in the Graph500 spec."]
fn validate(edges: [(node_id, node_id)],
            root: node_id, tree: bfs_result) -> bool {
    // There are 5 things to test. Below is code for each of them.

    // 1. The BFS tree is a tree and does not contain cycles.
    //
    // We do this by iterating over the tree, and tracing each of the
    // parent chains back to the root. While we do this, we also
    // compute the levels for each node.

    log(info, "Verifying tree structure...");

    let mut status = true;
    let level = tree.map() {|parent|
        let mut parent = parent;
        let mut path = [];

        if parent == -1 {
            // This node was not in the tree.
            -1
        }
        else {
            while parent != root {
                if vec::contains(path, parent) {
                    status = false;
                }

                path += [parent];
                parent = tree[parent];
            }

            // The length of the path back to the root is the current
            // level.
            path.len() as int
        }
    };

    if !status { ret status }

    // 2. Each tree edge connects vertices whose BFS levels differ by
    //    exactly one.

    log(info, "Verifying tree edges...");

    let status = tree.alli() {|k, parent|
        if parent != root && parent != -1 {
            level[parent] == level[k] - 1
        }
        else {
            true
        }
    };

    if !status { ret status }

    // 3. Every edge in the input list has vertices with levels that
    //    differ by at most one or that both are not in the BFS tree.

    log(info, "Verifying graph edges...");

    let status = edges.all() {|e|
        let (u, v) = e;

        abs(level[u] - level[v]) <= 1
    };

    if !status { ret status }

    // 4. The BFS tree spans an entire connected component's vertices.

    // This is harder. We'll skip it for now...

    // 5. A node and its parent are joined by an edge of the original
    //    graph.

    log(info, "Verifying tree and graph edges...");

    let status = tree.alli() {|u, v|
        if v == -1 || u as int == root {
            true
        }
        else {
            log(info, #fmt("Checking for %? or %?",
                           (u, v), (v, u)));
            edges.contains((u as int, v)) || edges.contains((v, u as int))
        }
    };

    if !status { ret status }

    // If we get through here, all the tests passed!
    true
}

fn main() {
    let scale = 14u;

    let start = time::precise_time_s();
    let edges = make_edges(scale, 16u);
    let stop = time::precise_time_s();

    io::stdout().write_line(#fmt("Generated %? edges in %? seconds.",
                                 vec::len(edges), stop - start));

    let start = time::precise_time_s();
    let graph = make_graph(1u << scale, edges);
    let stop = time::precise_time_s();

    let mut total_edges = 0u;
    vec::each(graph) {|edges| total_edges += edges.size(); true };

    io::stdout().write_line(#fmt("Generated graph with %? edges in %? seconds.",
                                 total_edges / 2u,
                                 stop - start));

    let root = 0;

    let start = time::precise_time_s();
    let bfs_tree = bfs(graph, root);
    let stop = time::precise_time_s();

    io::stdout().write_line(#fmt("BFS completed in %? seconds.",
                                 stop - start));

    let start = time::precise_time_s();
    assert(validate(graph, edges, root, bfs_tree));
    let stop = time::precise_time_s();

    io::stdout().write_line(#fmt("Validation completed in %? seconds.",
                                 stop - start));
}
<|endoftext|>


<commit_before>use std::f32::consts;
use std::env;

static RAD : f32 = 3.14;

mod circle_util {
pub fn get_radius() -> Option<f32> {
Some(3.45)
}
}

fn main() {

	let mut argv = env::args();
	let arg: String = argv.nth(1).unwrap();
	let env_rad : i32 = arg.parse().unwrap();

	// :: -> indicates the root crate, (this src file)
	let rad = ::circle_util::get_radius().unwrap_or(RAD);
	println!("The Area of Circle is {:?} (m)^2",consts::PI * rad * rad);
	println!("The Area of Circle is {:?} (m)^2",consts::PI * env_rad * env_rad);
	println!("{:?}", u32::from_str_radix(&"34",10).unwrap());
}
<commit_msg>updated chap2.rs<commit_after>#![warn(dead_code)]
#![warn(unused_assignments)]

use std::f32::consts;
static RAD : f32 = 3.14;

enum Optional {
	Something(i32),
	Nothing,
}

mod circle_util {

pub fn get_radius() -> Option<f32> {
Some(3.45)
}

pub fn get_minimum(v:Vec<i32>) -> i32 {
	let mut min = 9999;
	for i in v {
		if i<=min {min=i;}
		}
		min
}

}

fn main() {
let rad = ::circle_util::get_radius().unwrap_or(RAD);
println!("The Area of Circle is {:?} (m)^2",consts::PI*rad*rad);

//println!("{:?}",u32::from_str_radix(&"34",10).unwrap());
	let num_arr = vec![24,204,34,32,24,234];
	println!("The minimum from the list is  {:?}",::circle_util::get_minimum(num_arr));

	let str1 = "John";  //this is a string slice
	let str2 = "Doe" ;
	// to perform string concatenation
	let str3 = str1.to_string() + str2; //str3 is now a string

	println!("{:?}",str3+"c");

	// depicting truncation
	let flo = 34.999_f64;
	let lost = flo as i32;
	println!("{:?}",lost);

	struct Location<T> {
		x : T,
		y : T,
	}

	impl<T> Location<T> {
		fn get_location_name(self) -> &'static str {
			"Center"
		}
	}

	let l1 = Location {x:0,y:0};
	println!("{:?}",l1.get_location_name());

	/*type d_int = [[u32;4];4];
	let mut GRAPH : d_int = [[0,4,33,3],
								 [3,0,33,3],
								 [3,4,0,3],
								 [3,4,33,0]];

	fn add_weight(g:&d_int,u:i32,v:i32,w:i32) {
		g[u][v] = w;
	}
	add_weight(&GRAPH,2,1,99);
	println!("{:?}",GRAPH[2][1]);*/

	// integer overflow example
	let mut num:u16 = 1000;
	num = 65536;
	println!("{:?}",num);

	// memory addresses example
	let v = vec![1,2,4,5];
	let hex_addr = format!("{:p}",&v[0]);
	println!("{:?}",hex_addr);
	println!("{:p}",&v[1]);

	let y:&f64=&56.;
	println!("{:?}",y );
}
<|endoftext|>


<commit_before>use clap::crate_version;

use std::env;
use std::path::{Path, PathBuf};

use clap::{App, AppSettings, ArgMatches, SubCommand};

use mdbook::errors::Result as Result3;
use mdbook::MDBook;

fn main() {
    let d_message = "-d, --dest-dir=[dest-dir]
'The output directory for your book{n}(Defaults to ./book when omitted)'";
    let dir_message = "[dir]
'A directory for your book{n}(Defaults to Current Directory when omitted)'";

    let matches = App::new("rustbook")
        .about("Build a book with mdBook")
        .author("Steve Klabnik <steve@steveklabnik.com>")
        .version(&*format!("v{}", crate_version!()))
        .setting(AppSettings::SubcommandRequired)
        .subcommand(
            SubCommand::with_name("build")
                .about("Build the book from the markdown files")
                .arg_from_usage(d_message)
                .arg_from_usage(dir_message),
        )
        .subcommand(
            SubCommand::with_name("test")
                .about("Tests that a book's Rust code samples compile")
                .arg_from_usage(dir_message),
        )
        .subcommand(
            SubCommand::with_name("linkcheck")
                .about("Run linkcheck with mdBook 3")
                .arg_from_usage(dir_message),
        )
        .get_matches();

    // Check which subcomamnd the user ran...
    match matches.subcommand() {
        ("build", Some(sub_matches)) => {
            if let Err(e) = build(sub_matches) {
                handle_error(e);
            }
        }
        ("test", Some(sub_matches)) => {
            if let Err(e) = test(sub_matches) {
                handle_error(e);
            }
        }
        ("linkcheck", Some(sub_matches)) => {
            #[cfg(feature = "linkcheck")]
            {
                let (diags, files) = linkcheck(sub_matches).expect("Error while linkchecking.");
                if !diags.is_empty() {
                    let color = codespan_reporting::term::termcolor::ColorChoice::Auto;
                    let mut writer =
                        codespan_reporting::term::termcolor::StandardStream::stderr(color);
                    let cfg = codespan_reporting::term::Config::default();

                    for diag in diags {
                        codespan_reporting::term::emit(&mut writer, &cfg, &files, &diag)
                            .expect("Unable to emit linkcheck error.");
                    }

                    std::process::exit(101);
                }
            }

            #[cfg(not(feature = "linkcheck"))]
            {
                // This avoids the `unused_binding` lint.
                println!(
                    "mdbook-linkcheck is disabled, but arguments were passed: {:?}",
                    sub_matches
                );
            }
        }
        (_, _) => unreachable!(),
    };
}

#[cfg(feature = "linkcheck")]
pub fn linkcheck(
    args: &ArgMatches<'_>,
) -> Result<(Vec<codespan_reporting::diagnostic::Diagnostic>, codespan::Files), failure::Error> {
    use mdbook_linkcheck::Reason;

    let book_dir = get_book_dir(args);
    let src_dir = book_dir.join("src");
    let book = MDBook::load(&book_dir).unwrap();
    let linkck_cfg = mdbook_linkcheck::get_config(&book.config)?;
    let mut files = codespan::Files::new();
    let target_files = mdbook_linkcheck::load_files_into_memory(&book.book, &mut files);
    let cache = mdbook_linkcheck::Cache::default();

    let (links, incomplete) = mdbook_linkcheck::extract_links(target_files, &files);

    let outcome =
        mdbook_linkcheck::validate(&links, &linkck_cfg, &src_dir, &cache, &files, incomplete)?;

    let mut is_real_error = false;

    for link in outcome.invalid_links.iter() {
        match &link.reason {
            Reason::FileNotFound | Reason::TraversesParentDirectories => {
                is_real_error = true;
            }
            Reason::UnsuccessfulServerResponse(status) => {
                if status.is_client_error() {
                    is_real_error = true;
                } else {
                    eprintln!("Unsuccessful server response for link `{}`", link.link.uri);
                }
            }
            Reason::Client(err) => {
                if err.is_timeout() {
                    eprintln!("Timeout for link `{}`", link.link.uri);
                } else if err.is_server_error() {
                    eprintln!("Server error for link `{}`", link.link.uri);
                } else if !err.is_http() {
                    eprintln!("Non-HTTP-related error for link: {} {}", link.link.uri, err);
                } else {
                    is_real_error = true;
                }
            }
        }
    }

    if is_real_error {
        Ok((outcome.generate_diagnostics(&files, linkck_cfg.warning_policy), files))
    } else {
        Ok((vec![], files))
    }
}

// Build command implementation
pub fn build(args: &ArgMatches<'_>) -> Result3<()> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::load(&book_dir)?;

    // Set this to allow us to catch bugs in advance.
    book.config.build.create_missing = false;

    if let Some(dest_dir) = args.value_of("dest-dir") {
        book.config.build.build_dir = PathBuf::from(dest_dir);
    }

    book.build()?;

    Ok(())
}

fn test(args: &ArgMatches<'_>) -> Result3<()> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::load(&book_dir)?;
    book.test(vec![])
}

fn get_book_dir(args: &ArgMatches<'_>) -> PathBuf {
    if let Some(dir) = args.value_of("dir") {
        // Check if path is relative from current dir, or absolute...
        let p = Path::new(dir);
        if p.is_relative() { env::current_dir().unwrap().join(dir) } else { p.to_path_buf() }
    } else {
        env::current_dir().unwrap()
    }
}

fn handle_error(error: mdbook::errors::Error) -> ! {
    eprintln!("Error: {}", error);

    for cause in error.iter().skip(1) {
        eprintln!("\tCaused By: {}", cause);
    }

    ::std::process::exit(101);
}
<commit_msg>ignore 429's when linkcheck<commit_after>use clap::crate_version;

use std::env;
use std::path::{Path, PathBuf};

use clap::{App, AppSettings, ArgMatches, SubCommand};

use mdbook::errors::Result as Result3;
use mdbook::MDBook;

fn main() {
    let d_message = "-d, --dest-dir=[dest-dir]
'The output directory for your book{n}(Defaults to ./book when omitted)'";
    let dir_message = "[dir]
'A directory for your book{n}(Defaults to Current Directory when omitted)'";

    let matches = App::new("rustbook")
        .about("Build a book with mdBook")
        .author("Steve Klabnik <steve@steveklabnik.com>")
        .version(&*format!("v{}", crate_version!()))
        .setting(AppSettings::SubcommandRequired)
        .subcommand(
            SubCommand::with_name("build")
                .about("Build the book from the markdown files")
                .arg_from_usage(d_message)
                .arg_from_usage(dir_message),
        )
        .subcommand(
            SubCommand::with_name("test")
                .about("Tests that a book's Rust code samples compile")
                .arg_from_usage(dir_message),
        )
        .subcommand(
            SubCommand::with_name("linkcheck")
                .about("Run linkcheck with mdBook 3")
                .arg_from_usage(dir_message),
        )
        .get_matches();

    // Check which subcomamnd the user ran...
    match matches.subcommand() {
        ("build", Some(sub_matches)) => {
            if let Err(e) = build(sub_matches) {
                handle_error(e);
            }
        }
        ("test", Some(sub_matches)) => {
            if let Err(e) = test(sub_matches) {
                handle_error(e);
            }
        }
        ("linkcheck", Some(sub_matches)) => {
            #[cfg(feature = "linkcheck")]
            {
                let (diags, files) = linkcheck(sub_matches).expect("Error while linkchecking.");
                if !diags.is_empty() {
                    let color = codespan_reporting::term::termcolor::ColorChoice::Auto;
                    let mut writer =
                        codespan_reporting::term::termcolor::StandardStream::stderr(color);
                    let cfg = codespan_reporting::term::Config::default();

                    for diag in diags {
                        codespan_reporting::term::emit(&mut writer, &cfg, &files, &diag)
                            .expect("Unable to emit linkcheck error.");
                    }

                    std::process::exit(101);
                }
            }

            #[cfg(not(feature = "linkcheck"))]
            {
                // This avoids the `unused_binding` lint.
                println!(
                    "mdbook-linkcheck is disabled, but arguments were passed: {:?}",
                    sub_matches
                );
            }
        }
        (_, _) => unreachable!(),
    };
}

#[cfg(feature = "linkcheck")]
pub fn linkcheck(
    args: &ArgMatches<'_>,
) -> Result<(Vec<codespan_reporting::diagnostic::Diagnostic>, codespan::Files), failure::Error> {
    use mdbook_linkcheck::Reason;

    let book_dir = get_book_dir(args);
    let src_dir = book_dir.join("src");
    let book = MDBook::load(&book_dir).unwrap();
    let linkck_cfg = mdbook_linkcheck::get_config(&book.config)?;
    let mut files = codespan::Files::new();
    let target_files = mdbook_linkcheck::load_files_into_memory(&book.book, &mut files);
    let cache = mdbook_linkcheck::Cache::default();

    let (links, incomplete) = mdbook_linkcheck::extract_links(target_files, &files);

    let outcome =
        mdbook_linkcheck::validate(&links, &linkck_cfg, &src_dir, &cache, &files, incomplete)?;

    let mut is_real_error = false;

    for link in outcome.invalid_links.iter() {
        match &link.reason {
            Reason::FileNotFound | Reason::TraversesParentDirectories => {
                is_real_error = true;
            }
            Reason::UnsuccessfulServerResponse(status) => {
                if status.as_u16() == 429 {
                    eprintln!("Received 429 (TOO_MANY_REQUESTS) for link `{}`", link.link.uri);
                } else if status.is_client_error() {
                    is_real_error = true;
                } else {
                    eprintln!("Unsuccessful server response for link `{}`", link.link.uri);
                }
            }
            Reason::Client(err) => {
                if err.is_timeout() {
                    eprintln!("Timeout for link `{}`", link.link.uri);
                } else if err.is_server_error() {
                    eprintln!("Server error for link `{}`", link.link.uri);
                } else if !err.is_http() {
                    eprintln!("Non-HTTP-related error for link: {} {}", link.link.uri, err);
                } else {
                    is_real_error = true;
                }
            }
        }
    }

    if is_real_error {
        Ok((outcome.generate_diagnostics(&files, linkck_cfg.warning_policy), files))
    } else {
        Ok((vec![], files))
    }
}

// Build command implementation
pub fn build(args: &ArgMatches<'_>) -> Result3<()> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::load(&book_dir)?;

    // Set this to allow us to catch bugs in advance.
    book.config.build.create_missing = false;

    if let Some(dest_dir) = args.value_of("dest-dir") {
        book.config.build.build_dir = PathBuf::from(dest_dir);
    }

    book.build()?;

    Ok(())
}

fn test(args: &ArgMatches<'_>) -> Result3<()> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::load(&book_dir)?;
    book.test(vec![])
}

fn get_book_dir(args: &ArgMatches<'_>) -> PathBuf {
    if let Some(dir) = args.value_of("dir") {
        // Check if path is relative from current dir, or absolute...
        let p = Path::new(dir);
        if p.is_relative() { env::current_dir().unwrap().join(dir) } else { p.to_path_buf() }
    } else {
        env::current_dir().unwrap()
    }
}

fn handle_error(error: mdbook::errors::Error) -> ! {
    eprintln!("Error: {}", error);

    for cause in error.iter().skip(1) {
        eprintln!("\tCaused By: {}", cause);
    }

    ::std::process::exit(101);
}
<|endoftext|>


<commit_before>//! A shutdown channel.
//!
//! Each worker holds the `Sender` half. When all the `Sender` halves are
//! dropped, the `Receiver` receives a notification.

use crate::loom::sync::Arc;
use crate::sync::oneshot;

use std::time::Duration;

#[derive(Debug, Clone)]
pub(super) struct Sender {
    tx: Arc<oneshot::Sender<()>>,
}

#[derive(Debug)]
pub(super) struct Receiver {
    rx: oneshot::Receiver<()>,
}

pub(super) fn channel() -> (Sender, Receiver) {
    let (tx, rx) = oneshot::channel();
    let tx = Sender { tx: Arc::new(tx) };
    let rx = Receiver { rx };

    (tx, rx)
}

impl Receiver {
    /// Blocks the current thread until all `Sender` handles drop.
    ///
    /// If `timeout` is `Some`, the thread is blocked for **at most** `timeout`
    /// duration. If `timeout` is `None`, then the thread is blocked until the
    /// shutdown signal is received.
    ///
    /// If the timeout has elapsed, it returns `false`, otherwise it returns `true`.
    pub(crate) fn wait(&mut self, timeout: Option<Duration>) -> bool {
        use crate::runtime::enter::try_enter;

        if timeout == Some(Duration::from_nanos(0)) {
            return true;
        }

        let mut e = match try_enter(false) {
            Some(enter) => enter,
            _ => {
                if std::thread::panicking() {
                    // Don't panic in a panic
                    return false;
                } else {
                    panic!(
                        "Cannot drop a runtime in a context where blocking is not allowed. \
                        This happens when a runtime is dropped from within an asynchronous context."
                    );
                }
            }
        };

        // The oneshot completes with an Err
        //
        // If blocking fails to wait, this indicates a problem parking the
        // current thread (usually, shutting down a runtime stored in a
        // thread-local).
        if let Some(timeout) = timeout {
            e.block_on_timeout(&mut self.rx, timeout).is_ok()
        } else {
            let _ = e.block_on(&mut self.rx);
            true
        }
    }
}
<commit_msg>runtime: fix shutdown_timeout(0) blocking (#3174)<commit_after>//! A shutdown channel.
//!
//! Each worker holds the `Sender` half. When all the `Sender` halves are
//! dropped, the `Receiver` receives a notification.

use crate::loom::sync::Arc;
use crate::sync::oneshot;

use std::time::Duration;

#[derive(Debug, Clone)]
pub(super) struct Sender {
    tx: Arc<oneshot::Sender<()>>,
}

#[derive(Debug)]
pub(super) struct Receiver {
    rx: oneshot::Receiver<()>,
}

pub(super) fn channel() -> (Sender, Receiver) {
    let (tx, rx) = oneshot::channel();
    let tx = Sender { tx: Arc::new(tx) };
    let rx = Receiver { rx };

    (tx, rx)
}

impl Receiver {
    /// Blocks the current thread until all `Sender` handles drop.
    ///
    /// If `timeout` is `Some`, the thread is blocked for **at most** `timeout`
    /// duration. If `timeout` is `None`, then the thread is blocked until the
    /// shutdown signal is received.
    ///
    /// If the timeout has elapsed, it returns `false`, otherwise it returns `true`.
    pub(crate) fn wait(&mut self, timeout: Option<Duration>) -> bool {
        use crate::runtime::enter::try_enter;

        if timeout == Some(Duration::from_nanos(0)) {
            return false;
        }

        let mut e = match try_enter(false) {
            Some(enter) => enter,
            _ => {
                if std::thread::panicking() {
                    // Don't panic in a panic
                    return false;
                } else {
                    panic!(
                        "Cannot drop a runtime in a context where blocking is not allowed. \
                        This happens when a runtime is dropped from within an asynchronous context."
                    );
                }
            }
        };

        // The oneshot completes with an Err
        //
        // If blocking fails to wait, this indicates a problem parking the
        // current thread (usually, shutting down a runtime stored in a
        // thread-local).
        if let Some(timeout) = timeout {
            e.block_on_timeout(&mut self.rx, timeout).is_ok()
        } else {
            let _ = e.block_on(&mut self.rx);
            true
        }
    }
}
<|endoftext|>


<commit_before>//! Low level consumer wrapper.
extern crate rdkafka_sys as rdkafka;
extern crate futures;

use self::rdkafka::types::*;

use std::str;

use client::Client;
use config::{FromClientConfig, FromClientConfigAndContext, ClientConfig};
use consumer::{Consumer, ConsumerContext, CommitMode, EmptyConsumerContext};
use consumer::rebalance_cb;  // TODO: reorganize module
use error::{KafkaError, KafkaResult, IsError};
use metadata::Metadata;
use message::Message;
use util::cstr_to_owned;
use topic_partition_list::TopicPartitionList;

/// Low level wrapper around the librdkafka consumer. This consumer requires to be periodically polled
/// to make progress on rebalance, callbacks and to receive messages.
pub struct BaseConsumer<C: ConsumerContext> {
    client: Client<C>,
}

impl<C: ConsumerContext> Consumer<C> for BaseConsumer<C> {
    fn get_base_consumer(&self) -> &BaseConsumer<C> {
        self
    }

    fn get_base_consumer_mut(&mut self) -> &mut BaseConsumer<C> {
        self
    }
}

impl FromClientConfig for BaseConsumer<EmptyConsumerContext> {
    fn from_config(config: &ClientConfig) -> KafkaResult<BaseConsumer<EmptyConsumerContext>> {
        BaseConsumer::from_config_and_context(config, EmptyConsumerContext)
    }
}

/// Creates a new BaseConsumer starting from a ClientConfig.
impl<C: ConsumerContext> FromClientConfigAndContext<C> for BaseConsumer<C> {
    fn from_config_and_context(config: &ClientConfig, context: C) -> KafkaResult<BaseConsumer<C>> {
        let config_ptr = try!(config.create_native_config());
        unsafe { rdkafka::rd_kafka_conf_set_rebalance_cb(config_ptr, Some(rebalance_cb::<C>)) };
        let client = try!(Client::new(config_ptr, RDKafkaType::RD_KAFKA_CONSUMER, context));
        unsafe { rdkafka::rd_kafka_poll_set_consumer(client.native_ptr()) };
        Ok(BaseConsumer { client: client })
    }
}

impl<C: ConsumerContext> BaseConsumer<C> {
    /// Subscribes the consumer to a list of topics and/or topic sets (using regex).
    /// Strings starting with `^` will be regex-matched to the full list of topics in
    /// the cluster and matching topics will be added to the subscription list.
    pub fn subscribe(&mut self, topics: &Vec<&str>) -> KafkaResult<()> {
        let tp_list = TopicPartitionList::with_topics(topics).create_native_topic_partition_list();
        let ret_code = unsafe { rdkafka::rd_kafka_subscribe(self.client.native_ptr(), tp_list) };
        if ret_code.is_error() {
            let error = unsafe { cstr_to_owned(rdkafka::rd_kafka_err2str(ret_code)) };
            return Err(KafkaError::Subscription(error))
        };
        unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
        Ok(())
    }

    /// Unsubscribe from previous subscription list.
    pub fn unsubscribe(&mut self) {
        unsafe { rdkafka::rd_kafka_unsubscribe(self.client.native_ptr()) };
    }

    /// Manually assign topics and partitions to consume.
    pub fn assign(&mut self, assignment: &TopicPartitionList) -> KafkaResult<()> {
        let tp_list = assignment.create_native_topic_partition_list();
        let ret_code = unsafe { rdkafka::rd_kafka_assign(self.client.native_ptr(), tp_list) };
        if ret_code.is_error() {
            let error = unsafe { cstr_to_owned(rdkafka::rd_kafka_err2str(ret_code)) };
            return Err(KafkaError::Subscription(error))
        };
        unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
        Ok(())
    }

    /// Polls the consumer for events. It won't block more than the specified timeout.
    pub fn poll(&self, timeout_ms: i32) -> KafkaResult<Option<Message>> {
        let message_ptr = unsafe { rdkafka::rd_kafka_consumer_poll(self.client.native_ptr(), timeout_ms) };
        if message_ptr.is_null() {
            return Ok(None);
        }
        let error = unsafe { (*message_ptr).err };
        if error.is_error() {
            return Err(match error {
                rdkafka::rd_kafka_resp_err_t::RD_KAFKA_RESP_ERR__PARTITION_EOF => {
                    KafkaError::PartitionEOF(unsafe { (*message_ptr).partition } )
                },
                e => KafkaError::MessageConsumption(e)
            })
        }
        let kafka_message = Message::new(message_ptr);
        Ok(Some(kafka_message))
    }

    /// Commits the provided list of partitions. The commit can be sync (blocking), or async.
    pub fn commit(&self, topic_partition_list: &TopicPartitionList, mode: CommitMode) -> KafkaResult<()> {
        let tp_list = topic_partition_list.create_native_topic_partition_list();
        let error = unsafe {
            let e = rdkafka::rd_kafka_commit(self.client.native_ptr(), tp_list, mode as i32);
            rdkafka::rd_kafka_topic_partition_list_destroy(tp_list);
            e
        };
        if error.is_error() {
            Err(KafkaError::ConsumerCommit(error))
        } else {
            Ok(())
        }
    }

    /// Commits the specified message. The commit can be sync (blocking), or async.
    pub fn commit_message(&self, message: &Message, mode: CommitMode) -> KafkaResult<()> {
        let error = unsafe { rdkafka::rd_kafka_commit_message(self.client.native_ptr(), message.ptr(), mode as i32) };
        if error.is_error() {
            Err(KafkaError::ConsumerCommit(error))
        } else {
            Ok(())
        }
    }

    /// Returns the current topic subscription.
    pub fn subscription(&self) -> KafkaResult<TopicPartitionList> {
        let mut tp_list = unsafe { rdkafka::rd_kafka_topic_partition_list_new(0) };
        let error = unsafe {
            rdkafka::rd_kafka_subscription(self.client.native_ptr(), &mut tp_list)
        };

        if error.is_error() {
            unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
            Err(KafkaError::MetadataFetch(error))
        } else {
            Ok(TopicPartitionList::from_rdkafka(tp_list))
        }
    }

    /// Returns the current partition assignment.
    pub fn assignment(&self) -> KafkaResult<TopicPartitionList> {
        let mut tp_list = unsafe { rdkafka::rd_kafka_topic_partition_list_new(0) };
        let error = unsafe {
            rdkafka::rd_kafka_assignment(self.client.native_ptr(), &mut tp_list)
        };

        if error.is_error() {
            unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
            Err(KafkaError::MetadataFetch(error))
        } else {
            Ok(TopicPartitionList::from_rdkafka(tp_list))
        }
    }

    /// Retrieve committed offsets for topics and partitions.
    pub fn committed(&self, timeout_ms: i32) -> KafkaResult<TopicPartitionList> {
        let mut tp_list = unsafe { rdkafka::rd_kafka_topic_partition_list_new(0) };
        let assignment_error = unsafe {
            rdkafka::rd_kafka_assignment(self.client.native_ptr(), &mut tp_list)
        };
        if assignment_error.is_error() {
            unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
            return Err(KafkaError::MetadataFetch(assignment_error))
        }

        let committed_eror = unsafe {
            rdkafka::rd_kafka_committed(
                self.client.native_ptr(),
                tp_list,
                timeout_ms
            )
        };
        if committed_eror.is_error() {
            unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
            Err(KafkaError::MetadataFetch(committed_eror))
        } else {
            Ok(TopicPartitionList::from_rdkafka(tp_list))
        }
    }

    /// Retrieve current positions (offsets) for topics and partitions.
    pub fn position(&self) -> KafkaResult<TopicPartitionList> {
        let mut tp_list = unsafe { rdkafka::rd_kafka_topic_partition_list_new(0) };
        let error = unsafe {
            rdkafka::rd_kafka_assignment(self.client.native_ptr(), &mut tp_list);
            rdkafka::rd_kafka_position(
                self.client.native_ptr(),
                tp_list
            )
        };

        if error.is_error() {
            unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
            Err(KafkaError::MetadataFetch(error))
        } else {
            Ok(TopicPartitionList::from_rdkafka(tp_list))
        }
    }

    /// Returns the metadata information for all the topics in the cluster.
    pub fn fetch_metadata(&self, timeout_ms: i32) -> KafkaResult<Metadata> {
        self.client.fetch_metadata(timeout_ms)
    }

    /// Returns high and low watermark for the specified topic and partition.
    pub fn fetch_watermarks(&self, topic: &str, partition: i32, timeout_ms: i32) -> KafkaResult<(i64, i64)> {
        self.client.fetch_watermarks(topic, partition, timeout_ms)
    }
}

impl<C: ConsumerContext> Drop for BaseConsumer<C> {
    fn drop(&mut self) {
        trace!("Destroying consumer");  // TODO: fix me (multiple executions)
        unsafe { rdkafka::rd_kafka_consumer_close(self.client.native_ptr()) };
    }
}
<commit_msg>Fix memory leaks<commit_after>//! Low level consumer wrapper.
extern crate rdkafka_sys as rdkafka;
extern crate futures;

use self::rdkafka::types::*;

use std::str;

use client::Client;
use config::{FromClientConfig, FromClientConfigAndContext, ClientConfig};
use consumer::{Consumer, ConsumerContext, CommitMode, EmptyConsumerContext};
use consumer::rebalance_cb;  // TODO: reorganize module
use error::{KafkaError, KafkaResult, IsError};
use metadata::Metadata;
use message::Message;
use util::cstr_to_owned;
use topic_partition_list::TopicPartitionList;

/// Low level wrapper around the librdkafka consumer. This consumer requires to be periodically polled
/// to make progress on rebalance, callbacks and to receive messages.
pub struct BaseConsumer<C: ConsumerContext> {
    client: Client<C>,
}

impl<C: ConsumerContext> Consumer<C> for BaseConsumer<C> {
    fn get_base_consumer(&self) -> &BaseConsumer<C> {
        self
    }

    fn get_base_consumer_mut(&mut self) -> &mut BaseConsumer<C> {
        self
    }
}

impl FromClientConfig for BaseConsumer<EmptyConsumerContext> {
    fn from_config(config: &ClientConfig) -> KafkaResult<BaseConsumer<EmptyConsumerContext>> {
        BaseConsumer::from_config_and_context(config, EmptyConsumerContext)
    }
}

/// Creates a new BaseConsumer starting from a ClientConfig.
impl<C: ConsumerContext> FromClientConfigAndContext<C> for BaseConsumer<C> {
    fn from_config_and_context(config: &ClientConfig, context: C) -> KafkaResult<BaseConsumer<C>> {
        let config_ptr = try!(config.create_native_config());
        unsafe { rdkafka::rd_kafka_conf_set_rebalance_cb(config_ptr, Some(rebalance_cb::<C>)) };
        let client = try!(Client::new(config_ptr, RDKafkaType::RD_KAFKA_CONSUMER, context));
        unsafe { rdkafka::rd_kafka_poll_set_consumer(client.native_ptr()) };
        Ok(BaseConsumer { client: client })
    }
}

impl<C: ConsumerContext> BaseConsumer<C> {
    /// Subscribes the consumer to a list of topics and/or topic sets (using regex).
    /// Strings starting with `^` will be regex-matched to the full list of topics in
    /// the cluster and matching topics will be added to the subscription list.
    pub fn subscribe(&mut self, topics: &Vec<&str>) -> KafkaResult<()> {
        let tp_list = TopicPartitionList::with_topics(topics).create_native_topic_partition_list();
        let ret_code = unsafe { rdkafka::rd_kafka_subscribe(self.client.native_ptr(), tp_list) };
        if ret_code.is_error() {
            let error = unsafe { cstr_to_owned(rdkafka::rd_kafka_err2str(ret_code)) };
            return Err(KafkaError::Subscription(error))
        };
        unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
        Ok(())
    }

    /// Unsubscribe from previous subscription list.
    pub fn unsubscribe(&mut self) {
        unsafe { rdkafka::rd_kafka_unsubscribe(self.client.native_ptr()) };
    }

    /// Manually assign topics and partitions to consume.
    pub fn assign(&mut self, assignment: &TopicPartitionList) -> KafkaResult<()> {
        let tp_list = assignment.create_native_topic_partition_list();
        let ret_code = unsafe { rdkafka::rd_kafka_assign(self.client.native_ptr(), tp_list) };
        if ret_code.is_error() {
            let error = unsafe { cstr_to_owned(rdkafka::rd_kafka_err2str(ret_code)) };
            return Err(KafkaError::Subscription(error))
        };
        unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
        Ok(())
    }

    /// Polls the consumer for events. It won't block more than the specified timeout.
    pub fn poll(&self, timeout_ms: i32) -> KafkaResult<Option<Message>> {
        let message_ptr = unsafe { rdkafka::rd_kafka_consumer_poll(self.client.native_ptr(), timeout_ms) };
        if message_ptr.is_null() {
            return Ok(None);
        }
        let error = unsafe { (*message_ptr).err };
        if error.is_error() {
            return Err(match error {
                rdkafka::rd_kafka_resp_err_t::RD_KAFKA_RESP_ERR__PARTITION_EOF => {
                    KafkaError::PartitionEOF(unsafe { (*message_ptr).partition } )
                },
                e => KafkaError::MessageConsumption(e)
            })
        }
        let kafka_message = Message::new(message_ptr);
        Ok(Some(kafka_message))
    }

    /// Commits the provided list of partitions. The commit can be sync (blocking), or async.
    pub fn commit(&self, topic_partition_list: &TopicPartitionList, mode: CommitMode) -> KafkaResult<()> {
        let tp_list = topic_partition_list.create_native_topic_partition_list();
        let error = unsafe {
            let e = rdkafka::rd_kafka_commit(self.client.native_ptr(), tp_list, mode as i32);
            rdkafka::rd_kafka_topic_partition_list_destroy(tp_list);
            e
        };
        if error.is_error() {
            Err(KafkaError::ConsumerCommit(error))
        } else {
            Ok(())
        }
    }

    /// Commits the specified message. The commit can be sync (blocking), or async.
    pub fn commit_message(&self, message: &Message, mode: CommitMode) -> KafkaResult<()> {
        let error = unsafe { rdkafka::rd_kafka_commit_message(self.client.native_ptr(), message.ptr(), mode as i32) };
        if error.is_error() {
            Err(KafkaError::ConsumerCommit(error))
        } else {
            Ok(())
        }
    }

    /// Returns the current topic subscription.
    pub fn subscription(&self) -> KafkaResult<TopicPartitionList> {
        let mut tp_list = unsafe { rdkafka::rd_kafka_topic_partition_list_new(0) };
        let error = unsafe {
            rdkafka::rd_kafka_subscription(self.client.native_ptr(), &mut tp_list)
        };

        let result = if error.is_error() {
            Err(KafkaError::MetadataFetch(error))
        } else {
            Ok(TopicPartitionList::from_rdkafka(tp_list))
        };
        unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };

        result
    }

    /// Returns the current partition assignment.
    pub fn assignment(&self) -> KafkaResult<TopicPartitionList> {
        let mut tp_list = unsafe { rdkafka::rd_kafka_topic_partition_list_new(0) };
        let error = unsafe {
            rdkafka::rd_kafka_assignment(self.client.native_ptr(), &mut tp_list)
        };

        let result = if error.is_error() {
            Err(KafkaError::MetadataFetch(error))
        } else {
            Ok(TopicPartitionList::from_rdkafka(tp_list))
        };
        unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };

        result
    }

    /// Retrieve committed offsets for topics and partitions.
    pub fn committed(&self, timeout_ms: i32) -> KafkaResult<TopicPartitionList> {
        let mut tp_list = unsafe { rdkafka::rd_kafka_topic_partition_list_new(0) };
        let assignment_error = unsafe {
            rdkafka::rd_kafka_assignment(self.client.native_ptr(), &mut tp_list)
        };
        if assignment_error.is_error() {
            unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };
            return Err(KafkaError::MetadataFetch(assignment_error))
        }

        let committed_error = unsafe {
            rdkafka::rd_kafka_committed(
                self.client.native_ptr(),
                tp_list,
                timeout_ms
            )
        };

        let result = if committed_error.is_error() {
            Err(KafkaError::MetadataFetch(committed_error))
        } else {
            Ok(TopicPartitionList::from_rdkafka(tp_list))
        };
        unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };

        result
    }

    /// Retrieve current positions (offsets) for topics and partitions.
    pub fn position(&self) -> KafkaResult<TopicPartitionList> {
        let mut tp_list = unsafe { rdkafka::rd_kafka_topic_partition_list_new(0) };
        let error = unsafe {
            rdkafka::rd_kafka_assignment(self.client.native_ptr(), &mut tp_list);
            rdkafka::rd_kafka_position(
                self.client.native_ptr(),
                tp_list
            )
        };

        let result = if error.is_error() {
            Err(KafkaError::MetadataFetch(error))
        } else {
            Ok(TopicPartitionList::from_rdkafka(tp_list))
        };
        unsafe { rdkafka::rd_kafka_topic_partition_list_destroy(tp_list) };

        result
    }

    /// Returns the metadata information for all the topics in the cluster.
    pub fn fetch_metadata(&self, timeout_ms: i32) -> KafkaResult<Metadata> {
        self.client.fetch_metadata(timeout_ms)
    }

    /// Returns high and low watermark for the specified topic and partition.
    pub fn fetch_watermarks(&self, topic: &str, partition: i32, timeout_ms: i32) -> KafkaResult<(i64, i64)> {
        self.client.fetch_watermarks(topic, partition, timeout_ms)
    }
}

impl<C: ConsumerContext> Drop for BaseConsumer<C> {
    fn drop(&mut self) {
        trace!("Destroying consumer");  // TODO: fix me (multiple executions)
        unsafe { rdkafka::rd_kafka_consumer_close(self.client.native_ptr()) };
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::hash;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;

use {Encode, EncodePtr, Message, ToMessage};

/// A type used to mark that a struct owns the object(s) it contains,
/// so it has the sole references to them.
pub enum Owned { }
/// A type used to mark that the object(s) a struct contains are shared,
/// so there may be other references to them.
pub enum Shared { }

/// A type that marks what type of ownership a struct has over the object(s)
/// it contains; specifically, either `Owned` or `Shared`.
pub trait Ownership { }
impl Ownership for Owned { }
impl Ownership for Shared { }

/// A pointer type for Objective-C's reference counted objects. The object of
/// an `Id` is retained and sent a `release` message when the `Id` is dropped.
///
/// An `Id` may be either `Owned` or `Shared`, represented by the types `Id`
/// and `ShareId`, respectively. If owned, there are no other references to the
/// object and the `Id` can be mutably dereferenced. `ShareId`, however, can
/// only be immutably dereferenced because there may be other references to the
/// object, but a `ShareId` can be cloned to provide more references to the
/// object. An owned `Id` can be "downgraded" freely to a `ShareId`, but there
/// is no way to safely upgrade back.
#[unsafe_no_drop_flag]
pub struct Id<T, O = Owned> where T: Message, O: Ownership {
    ptr: *mut T,
}

impl<T, O> Id<T, O> where T: Message, O: Ownership {
    /// Constructs an `Id` from a pointer to an unretained object and
    /// retains it. Panics if the pointer is null.
    /// Unsafe because the pointer must be to a valid object and
    /// the caller must ensure the ownership is correct.
    pub unsafe fn from_ptr(ptr: *mut T) -> Id<T, O> {
        match Id::maybe_from_ptr(ptr) {
            Some(id) => id,
            None => panic!("Attempted to construct an Id from a null pointer"),
        }
    }

    /// Constructs an `Id` from a pointer to a retained object; this won't
    /// retain the pointer, so the caller must ensure the object has a +1
    /// retain count. Panics if the pointer is null.
    /// Unsafe because the pointer must be to a valid object and
    /// the caller must ensure the ownership is correct.
    pub unsafe fn from_retained_ptr(ptr: *mut T) -> Id<T, O> {
        match Id::maybe_from_retained_ptr(ptr) {
            Some(id) => id,
            None => panic!("Attempted to construct an Id from a null pointer"),
        }
    }

    /// Constructs an `Id` from a pointer to an unretained object and
    /// retains it if the pointer isn't null, otherwise returns None.
    /// Unsafe because the pointer must be to a valid object and
    /// the caller must ensure the ownership is correct.
    pub unsafe fn maybe_from_ptr(ptr: *mut T) -> Option<Id<T, O>> {
        if ptr.is_null() {
            None
        } else {
            let ptr = msg_send![ptr, retain];
            Some(Id { ptr: ptr })
        }
    }

    /// Constructs an `Id` from a pointer to a retained object if the pointer
    /// isn't null, otherwise returns None. This won't retain the pointer,
    /// so the caller must ensure the object has a +1 retain count.
    /// Unsafe because the pointer must be to a valid object and
    /// the caller must ensure the ownership is correct.
    pub unsafe fn maybe_from_retained_ptr(ptr: *mut T) -> Option<Id<T, O>> {
        if ptr.is_null() {
            None
        } else {
            Some(Id { ptr: ptr })
        }
    }
}

impl<T> Id<T, Owned> where T: Message {
    /// "Downgrade" an owned `Id` to a `ShareId`, allowing it to be cloned.
    pub fn share(self) -> ShareId<T> {
        let ptr = self.ptr;
        unsafe {
            mem::forget(self);
        }
        Id { ptr: ptr }
    }
}

impl<T, O> Encode for Id<T, O> where T: Message, O: Ownership {
    fn code() -> &'static str {
        <T as EncodePtr>::ptr_code()
    }
}

impl<T, O> ToMessage for Id<T, O> where T: Message, O: Ownership {
    type Target = T;

    fn as_ptr(&self) -> *mut T {
        self.ptr
    }
}

impl<T> Clone for Id<T, Shared> where T: Message {
    fn clone(&self) -> ShareId<T> {
        let ptr = unsafe {
            msg_send![self.ptr, retain]
        };
        Id { ptr: ptr }
    }
}

#[unsafe_destructor]
impl<T, O> Drop for Id<T, O> where T: Message, O: Ownership {
    fn drop(&mut self) {
        if !self.ptr.is_null() {
            let ptr = mem::replace(&mut self.ptr, ptr::null_mut());
            unsafe {
                let _: () = msg_send![ptr, release];
            }
        }
    }
}

impl<T, O> Deref for Id<T, O> where T: Message, O: Ownership {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.ptr }
    }
}

impl<T> DerefMut for Id<T, Owned> where T: Message {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.ptr }
    }
}

impl<T, O> PartialEq for Id<T, O> where T: Message + PartialEq, O: Ownership {
    fn eq(&self, other: &Id<T, O>) -> bool {
        self.deref() == other.deref()
    }

    fn ne(&self, other: &Id<T, O>) -> bool {
        self.deref() != other.deref()
    }
}

impl<T, O> Eq for Id<T, O> where T: Message + Eq, O: Ownership { }

impl<H, T, O> hash::Hash<H> for Id<T, O>
        where H: hash::Hasher, T: Message + hash::Hash<H>, O: Ownership {
    fn hash(&self, state: &mut H) {
        self.deref().hash(state)
    }
}

impl<T, O> fmt::Debug for Id<T, O> where T: Message + fmt::Debug, O: Ownership {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.deref().fmt(f)
    }
}

/// A convenient alias for a shared `Id`.
pub type ShareId<T> = Id<T, Shared>;

/// Extension methods for slices containing `Id`s.
pub trait IdSlice {
    type Item;

    /// Convert a slice of `Id`s into a slice of references
    fn as_refs_slice(&self) -> &[&Self::Item];
}

impl<T, O> IdSlice for [Id<T, O>] where T: Message, O: Ownership {
    type Item = T;

    fn as_refs_slice(&self) -> &[&T] {
        unsafe {
            mem::transmute(self)
        }
    }
}
<commit_msg>Use objc_retain/release functions instead of messages.<commit_after>use std::fmt;
use std::hash;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;

use {Encode, EncodePtr, Message, ToMessage};
use runtime::Object;

#[link(name = "Foundation", kind = "framework")]
extern {
    fn objc_retain(obj: *mut Object) -> *mut Object;
    fn objc_release(obj: *mut Object);
}

/// A type used to mark that a struct owns the object(s) it contains,
/// so it has the sole references to them.
pub enum Owned { }
/// A type used to mark that the object(s) a struct contains are shared,
/// so there may be other references to them.
pub enum Shared { }

/// A type that marks what type of ownership a struct has over the object(s)
/// it contains; specifically, either `Owned` or `Shared`.
pub trait Ownership { }
impl Ownership for Owned { }
impl Ownership for Shared { }

/// A pointer type for Objective-C's reference counted objects. The object of
/// an `Id` is retained and sent a `release` message when the `Id` is dropped.
///
/// An `Id` may be either `Owned` or `Shared`, represented by the types `Id`
/// and `ShareId`, respectively. If owned, there are no other references to the
/// object and the `Id` can be mutably dereferenced. `ShareId`, however, can
/// only be immutably dereferenced because there may be other references to the
/// object, but a `ShareId` can be cloned to provide more references to the
/// object. An owned `Id` can be "downgraded" freely to a `ShareId`, but there
/// is no way to safely upgrade back.
#[unsafe_no_drop_flag]
pub struct Id<T, O = Owned> where T: Message, O: Ownership {
    ptr: *mut T,
}

impl<T, O> Id<T, O> where T: Message, O: Ownership {
    /// Constructs an `Id` from a pointer to an unretained object and
    /// retains it. Panics if the pointer is null.
    /// Unsafe because the pointer must be to a valid object and
    /// the caller must ensure the ownership is correct.
    pub unsafe fn from_ptr(ptr: *mut T) -> Id<T, O> {
        match Id::maybe_from_ptr(ptr) {
            Some(id) => id,
            None => panic!("Attempted to construct an Id from a null pointer"),
        }
    }

    /// Constructs an `Id` from a pointer to a retained object; this won't
    /// retain the pointer, so the caller must ensure the object has a +1
    /// retain count. Panics if the pointer is null.
    /// Unsafe because the pointer must be to a valid object and
    /// the caller must ensure the ownership is correct.
    pub unsafe fn from_retained_ptr(ptr: *mut T) -> Id<T, O> {
        match Id::maybe_from_retained_ptr(ptr) {
            Some(id) => id,
            None => panic!("Attempted to construct an Id from a null pointer"),
        }
    }

    /// Constructs an `Id` from a pointer to an unretained object and
    /// retains it if the pointer isn't null, otherwise returns None.
    /// Unsafe because the pointer must be to a valid object and
    /// the caller must ensure the ownership is correct.
    pub unsafe fn maybe_from_ptr(ptr: *mut T) -> Option<Id<T, O>> {
        if ptr.is_null() {
            None
        } else {
            let ptr = objc_retain(ptr as *mut Object) as *mut T;
            Some(Id { ptr: ptr })
        }
    }

    /// Constructs an `Id` from a pointer to a retained object if the pointer
    /// isn't null, otherwise returns None. This won't retain the pointer,
    /// so the caller must ensure the object has a +1 retain count.
    /// Unsafe because the pointer must be to a valid object and
    /// the caller must ensure the ownership is correct.
    pub unsafe fn maybe_from_retained_ptr(ptr: *mut T) -> Option<Id<T, O>> {
        if ptr.is_null() {
            None
        } else {
            Some(Id { ptr: ptr })
        }
    }
}

impl<T> Id<T, Owned> where T: Message {
    /// "Downgrade" an owned `Id` to a `ShareId`, allowing it to be cloned.
    pub fn share(self) -> ShareId<T> {
        let ptr = self.ptr;
        unsafe {
            mem::forget(self);
        }
        Id { ptr: ptr }
    }
}

impl<T, O> Encode for Id<T, O> where T: Message, O: Ownership {
    fn code() -> &'static str {
        <T as EncodePtr>::ptr_code()
    }
}

impl<T, O> ToMessage for Id<T, O> where T: Message, O: Ownership {
    type Target = T;

    fn as_ptr(&self) -> *mut T {
        self.ptr
    }
}

impl<T> Clone for Id<T, Shared> where T: Message {
    fn clone(&self) -> ShareId<T> {
        let ptr = unsafe {
            objc_retain(self.ptr as *mut Object) as *mut T
        };
        Id { ptr: ptr }
    }
}

#[unsafe_destructor]
impl<T, O> Drop for Id<T, O> where T: Message, O: Ownership {
    fn drop(&mut self) {
        if !self.ptr.is_null() {
            let ptr = mem::replace(&mut self.ptr, ptr::null_mut());
            unsafe {
                objc_release(ptr as *mut Object);
            }
        }
    }
}

impl<T, O> Deref for Id<T, O> where T: Message, O: Ownership {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.ptr }
    }
}

impl<T> DerefMut for Id<T, Owned> where T: Message {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.ptr }
    }
}

impl<T, O> PartialEq for Id<T, O> where T: Message + PartialEq, O: Ownership {
    fn eq(&self, other: &Id<T, O>) -> bool {
        self.deref() == other.deref()
    }

    fn ne(&self, other: &Id<T, O>) -> bool {
        self.deref() != other.deref()
    }
}

impl<T, O> Eq for Id<T, O> where T: Message + Eq, O: Ownership { }

impl<H, T, O> hash::Hash<H> for Id<T, O>
        where H: hash::Hasher, T: Message + hash::Hash<H>, O: Ownership {
    fn hash(&self, state: &mut H) {
        self.deref().hash(state)
    }
}

impl<T, O> fmt::Debug for Id<T, O> where T: Message + fmt::Debug, O: Ownership {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.deref().fmt(f)
    }
}

/// A convenient alias for a shared `Id`.
pub type ShareId<T> = Id<T, Shared>;

/// Extension methods for slices containing `Id`s.
pub trait IdSlice {
    type Item;

    /// Convert a slice of `Id`s into a slice of references
    fn as_refs_slice(&self) -> &[&Self::Item];
}

impl<T, O> IdSlice for [Id<T, O>] where T: Message, O: Ownership {
    type Item = T;

    fn as_refs_slice(&self) -> &[&T] {
        unsafe {
            mem::transmute(self)
        }
    }
}
<|endoftext|>