<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

// SVG 1.1 (Second Edition)
// https://www.w3.org/TR/SVG/
<% data.new_style_struct("InheritedSVG",
                         inherited=True,
                         gecko_name="SVG") %>

// TODO(emilio): Should some of these types be animatable?

// Section 10 - Text

${helpers.single_keyword("text-anchor",
                         "start middle end",
                         products="gecko",
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty")}

// Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("color-interpolation",
                         "srgb auto linearrgb",
                         products="gecko",
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")}

${helpers.single_keyword("color-interpolation-filters", "linearrgb auto srgb",
                         products="gecko",
                         gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")}

${helpers.predefined_type(
    "fill", "SVGPaint",
    "::values::computed::SVGPaint::black()",
    products="gecko",
    animation_type="none",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")}

${helpers.predefined_type("fill-opacity", "Opacity", "1.0",
                          products="gecko", animation_type="none",
                          spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")}

${helpers.single_keyword("fill-rule", "nonzero evenodd",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         products="gecko", animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")}

${helpers.single_keyword("shape-rendering",
                         "auto optimizespeed crispedges geometricprecision",
                         products="gecko",
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")}

${helpers.predefined_type(
    "stroke", "SVGPaint",
    "Default::default()",
    products="gecko",
    animation_type="none",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}

${helpers.predefined_type(
    "stroke-width", "LengthOrPercentage",
    "computed::LengthOrPercentage::one()",
    "parse_numbers_are_pixels_non_negative",
    products="gecko",
    animation_type="normal",
    needs_context=False,
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}

${helpers.single_keyword("stroke-linecap", "butt round square",
                         products="gecko", animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}

${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                         products="gecko", animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}

${helpers.predefined_type("stroke-miterlimit", "Number", "4.0",
                          "parse_at_least_one", products="gecko",
                          needs_context=False,
                          animation_type="none",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}

${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
                          products="gecko", animation_type="none",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}

${helpers.predefined_type("stroke-dasharray",
                          "LengthOrPercentageOrNumber",
                          "Either::Second(0.0)",
                          "parse_non_negative",
                          vector="True",
                          products="gecko",
                          animation_type="none",
                          space_separated_allowed="True",
                          spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

${helpers.predefined_type(
    "stroke-dashoffset", "LengthOrPercentage",
    "computed::LengthOrPercentage::zero()",
    "parse_numbers_are_pixels",
    products="gecko",
    animation_type="normal",
    needs_context=False,
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

// Section 14 - Clipping, Masking and Compositing
${helpers.single_keyword("clip-rule", "nonzero evenodd",
                         products="gecko",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}

${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          animation_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          animation_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          animation_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

<%helpers:longhand name="paint-order"
                   animation_type="none"
                   products="gecko"
                   spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">

    use values::computed::ComputedValueAsSpecified;
    use std::fmt;
    use style_traits::ToCss;
    use values::HasViewportPercentage;

    pub const NORMAL: u8 = 0;
    pub const FILL: u8 = 1;
    pub const STROKE: u8 = 2;
    pub const MARKERS: u8 = 3;

    // number of bits for each component
    pub const SHIFT: u8 = 2;
    // mask with above bits set
    pub const MASK: u8 = 0b11;
    // number of non-normal keyword values
    pub const COUNT: u8 = 3;
    // all keywords
    pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];

    /// Represented as a six-bit field, of 3 two-bit pairs
    ///
    /// Each pair can be set to FILL, STROKE, or MARKERS
    /// Lowest significant bit pairs are highest priority.
    ///  `normal` is the empty bitfield. The three pairs are
    /// never zero in any case other than `normal`.
    ///
    /// Higher priority values, i.e. the values specified first,
    /// will be painted first (and may be covered by paintings of lower priority)
    #[derive(PartialEq, Clone, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue(pub u8);

    pub mod computed_value {
        pub use super::SpecifiedValue as T;
    }

    pub fn get_initial_value() -> SpecifiedValue {
      SpecifiedValue(NORMAL)
    }

    impl SpecifiedValue {
        pub fn bits_at(&self, pos: u8) -> u8 {
            (self.0 >> pos * SHIFT) & MASK
        }
    }

    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            Ok(SpecifiedValue(0))
        } else {
            let mut value = 0;
            // bitfield representing what we've seen so far
            // bit 1 is fill, bit 2 is stroke, bit 3 is markers
            let mut seen = 0;
            let mut pos = 0;

            loop {

                let result = input.try(|i| {
                    match_ignore_ascii_case! { &i.expect_ident()?,
                        "fill" => Ok(FILL),
                        "stroke" => Ok(STROKE),
                        "markers" => Ok(MARKERS),
                        _ => Err(())
                    }
                });

                match result {
                    Ok(val) => {
                        if (seen & (1 << val)) != 0 {
                            // don't parse the same ident twice
                            return Err(())
                        } else {
                            value |= val << (pos * SHIFT);
                            seen |= 1 << val;
                            pos += 1;
                        }
                    }
                    Err(()) => break,
                }
            }

            if value == 0 {
                // couldn't find any keyword
                Err(())
            } else {
                // fill in rest
                for i in pos..COUNT {
                    for paint in &ALL {
                        // if not seen, set bit at position, mark as seen
                        if (seen & (1 << paint)) == 0 {
                            seen |= 1 << paint;
                            value |= paint << (i * SHIFT);
                            break;
                        }
                    }
                }

                Ok(SpecifiedValue(value))
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0 == 0 {
                return dest.write_str("normal")
            }

            for pos in 0..COUNT {
                if pos != 0 {
                    dest.write_str(" ")?
                }
                match self.bits_at(pos) {
                    FILL => dest.write_str("fill")?,
                    STROKE => dest.write_str("stroke")?,
                    MARKERS => dest.write_str("markers")?,
                    _ => unreachable!(),
                }
            }
            Ok(())
        }
    }

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }
</%helpers:longhand>

<commit_msg>Support stroke-dasharray: none<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

// SVG 1.1 (Second Edition)
// https://www.w3.org/TR/SVG/
<% data.new_style_struct("InheritedSVG",
                         inherited=True,
                         gecko_name="SVG") %>

// TODO(emilio): Should some of these types be animatable?

// Section 10 - Text

${helpers.single_keyword("text-anchor",
                         "start middle end",
                         products="gecko",
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty")}

// Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("color-interpolation",
                         "srgb auto linearrgb",
                         products="gecko",
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")}

${helpers.single_keyword("color-interpolation-filters", "linearrgb auto srgb",
                         products="gecko",
                         gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")}

${helpers.predefined_type(
    "fill", "SVGPaint",
    "::values::computed::SVGPaint::black()",
    products="gecko",
    animation_type="none",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")}

${helpers.predefined_type("fill-opacity", "Opacity", "1.0",
                          products="gecko", animation_type="none",
                          spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")}

${helpers.single_keyword("fill-rule", "nonzero evenodd",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         products="gecko", animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")}

${helpers.single_keyword("shape-rendering",
                         "auto optimizespeed crispedges geometricprecision",
                         products="gecko",
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")}

${helpers.predefined_type(
    "stroke", "SVGPaint",
    "Default::default()",
    products="gecko",
    animation_type="none",
    boxed=True,
    spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}

${helpers.predefined_type(
    "stroke-width", "LengthOrPercentage",
    "computed::LengthOrPercentage::one()",
    "parse_numbers_are_pixels_non_negative",
    products="gecko",
    animation_type="normal",
    needs_context=False,
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}

${helpers.single_keyword("stroke-linecap", "butt round square",
                         products="gecko", animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}

${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                         products="gecko", animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}

${helpers.predefined_type("stroke-miterlimit", "Number", "4.0",
                          "parse_at_least_one", products="gecko",
                          needs_context=False,
                          animation_type="none",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}

${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
                          products="gecko", animation_type="none",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}

${helpers.predefined_type("stroke-dasharray",
                          "LengthOrPercentageOrNumber",
                          "Either::Second(0.0)",
                          "parse_non_negative",
                          vector="True",
                          allow_empty="True",
                          products="gecko",
                          animation_type="none",
                          space_separated_allowed="True",
                          spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

${helpers.predefined_type(
    "stroke-dashoffset", "LengthOrPercentage",
    "computed::LengthOrPercentage::zero()",
    "parse_numbers_are_pixels",
    products="gecko",
    animation_type="normal",
    needs_context=False,
    spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}

// Section 14 - Clipping, Masking and Compositing
${helpers.single_keyword("clip-rule", "nonzero evenodd",
                         products="gecko",
                         gecko_enum_prefix="StyleFillRule",
                         gecko_inexhaustive=True,
                         animation_type="none",
                         spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}

${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          animation_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          animation_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
                          products="gecko",
                          animation_type="none",
                          spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}

<%helpers:longhand name="paint-order"
                   animation_type="none"
                   products="gecko"
                   spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder">

    use values::computed::ComputedValueAsSpecified;
    use std::fmt;
    use style_traits::ToCss;
    use values::HasViewportPercentage;

    pub const NORMAL: u8 = 0;
    pub const FILL: u8 = 1;
    pub const STROKE: u8 = 2;
    pub const MARKERS: u8 = 3;

    // number of bits for each component
    pub const SHIFT: u8 = 2;
    // mask with above bits set
    pub const MASK: u8 = 0b11;
    // number of non-normal keyword values
    pub const COUNT: u8 = 3;
    // all keywords
    pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];

    /// Represented as a six-bit field, of 3 two-bit pairs
    ///
    /// Each pair can be set to FILL, STROKE, or MARKERS
    /// Lowest significant bit pairs are highest priority.
    ///  `normal` is the empty bitfield. The three pairs are
    /// never zero in any case other than `normal`.
    ///
    /// Higher priority values, i.e. the values specified first,
    /// will be painted first (and may be covered by paintings of lower priority)
    #[derive(PartialEq, Clone, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue(pub u8);

    pub mod computed_value {
        pub use super::SpecifiedValue as T;
    }

    pub fn get_initial_value() -> SpecifiedValue {
      SpecifiedValue(NORMAL)
    }

    impl SpecifiedValue {
        pub fn bits_at(&self, pos: u8) -> u8 {
            (self.0 >> pos * SHIFT) & MASK
        }
    }

    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            Ok(SpecifiedValue(0))
        } else {
            let mut value = 0;
            // bitfield representing what we've seen so far
            // bit 1 is fill, bit 2 is stroke, bit 3 is markers
            let mut seen = 0;
            let mut pos = 0;

            loop {

                let result = input.try(|i| {
                    match_ignore_ascii_case! { &i.expect_ident()?,
                        "fill" => Ok(FILL),
                        "stroke" => Ok(STROKE),
                        "markers" => Ok(MARKERS),
                        _ => Err(())
                    }
                });

                match result {
                    Ok(val) => {
                        if (seen & (1 << val)) != 0 {
                            // don't parse the same ident twice
                            return Err(())
                        } else {
                            value |= val << (pos * SHIFT);
                            seen |= 1 << val;
                            pos += 1;
                        }
                    }
                    Err(()) => break,
                }
            }

            if value == 0 {
                // couldn't find any keyword
                Err(())
            } else {
                // fill in rest
                for i in pos..COUNT {
                    for paint in &ALL {
                        // if not seen, set bit at position, mark as seen
                        if (seen & (1 << paint)) == 0 {
                            seen |= 1 << paint;
                            value |= paint << (i * SHIFT);
                            break;
                        }
                    }
                }

                Ok(SpecifiedValue(value))
            }
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.0 == 0 {
                return dest.write_str("normal")
            }

            for pos in 0..COUNT {
                if pos != 0 {
                    dest.write_str(" ")?
                }
                match self.bits_at(pos) {
                    FILL => dest.write_str("fill")?,
                    STROKE => dest.write_str("stroke")?,
                    MARKERS => dest.write_str("markers")?,
                    _ => unreachable!(),
                }
            }
            Ok(())
        }
    }

    no_viewport_percentage!(SpecifiedValue);

    impl ComputedValueAsSpecified for SpecifiedValue { }
</%helpers:longhand>

<|endoftext|>


<commit_before>use super::RootMove;
use super::super::MAX_THREADS;
use super::root_moves_list::{RootMoveList,RawRootMoveList};

use std::heap::{Alloc, Layout, Heap};
use std::ptr::Unique;
use std::ptr::Shared;
use std::sync::Arc;
use std::sync::atomic::{Ordering,AtomicUsize,fence,compiler_fence};
use std::mem;

use std::iter::{Iterator,IntoIterator};

use pleco::Board;
use pleco::board::movegen::{MoveGen,Legal};
use pleco::core::mono_traits::AllGenType;

pub type RawRmManager = [RawRootMoveList; MAX_THREADS];

pub struct RmManager {
    threads: Arc<AtomicUsize>,
    moves: Shared<RawRmManager>,
    ref_count: Arc<AtomicUsize>
}

unsafe impl Send for RmManager {}

impl Clone for RmManager {
    fn clone(&self) -> Self {
        let ref_count: Arc<AtomicUsize> = self.ref_count.clone();
        ref_count.fetch_add(1, Ordering::SeqCst);
        RmManager {
            threads: self.threads.clone(),
            moves: self.moves.clone(),
            ref_count
        }
    }
}

impl RmManager {
    pub fn new() -> Self {
        let mut rms = RmManager::init();
        rms.allocate();
        unsafe { rms.set_states();}
        rms
    }

    fn init() -> Self {
        RmManager {
            threads: Arc::new(AtomicUsize::new(0)),
            moves: Shared::empty(),
            ref_count: Arc::new(AtomicUsize::new(1))
        }
    }

    fn allocate(&mut self) {
        unsafe {
            let elem_size = mem::size_of::<RawRootMoveList>();
            let alloc_size = elem_size * MAX_THREADS;
            let align = mem::align_of::<RawRootMoveList>();
            let layout = Layout::from_size_align(alloc_size, align).unwrap();
            let result = Heap.alloc_zeroed(layout);
            let new_ptr = match result {
                Ok(ptr) => ptr,
                Err(err) => Heap.oom(err),
            };
            {
                let location: usize = mem::transmute::<*mut u8, usize>(new_ptr);
                println!("Bytes at {:x}", location);
            }
            self.moves = Shared::new(new_ptr as *mut RawRmManager).unwrap();
        }
    }

    unsafe fn set_states(&mut self) {
        for x in 0..MAX_THREADS {
            let line =  self.as_raw_ptr().offset(x as isize);
            let location: usize = mem::transmute::<*mut RawRootMoveList, usize>(line);
            println!("This x at {:x}", location);
            let mut raw_list = self.get_list_unchecked(x);
            raw_list.init();
        }
    }

    pub fn threads(&self) -> usize {
        self.threads.load(Ordering::Relaxed)
    }

    pub fn add_thread(&mut self) -> Option<RootMoveList> {
        if self.threads() >= MAX_THREADS {
            None
        } else {
            let thread_idx = self.threads.fetch_add(1, Ordering::SeqCst);
            unsafe {
                Some(self.get_list_unchecked(thread_idx))
            }
        }
    }

    pub fn get_list(&self, num: usize) -> Option<RootMoveList> {
        if num >= self.threads() {
            None
        } else {
            unsafe {
                Some(self.get_list_unchecked(num))
            }
        }
    }

    pub unsafe fn get_list_unchecked(&self, num: usize) -> RootMoveList {
        RootMoveList {
            moves: self.as_raw_ptr().offset(num as isize)
        }
    }

    pub unsafe fn replace_moves(&mut self, board: &Board) {
        let legal_moves = MoveGen::generate::<Legal, AllGenType>(&board);
        let mut first = self.as_ptr();
        first.replace(&legal_moves);
        let num = self.threads();
        for i in 1..num {
            self.get_list_unchecked(i).clone_from_other(&first);
        }
    }

    pub unsafe fn as_ptr(&self) -> RootMoveList {
        RootMoveList {
            moves: mem::transmute::<*mut RawRmManager, *mut RawRootMoveList>(self.moves.as_ptr())
        }
    }

    unsafe fn as_raw_ptr(&self) -> *mut RawRootMoveList {
        mem::transmute::<*mut RawRmManager, *mut RawRootMoveList>(self.moves.as_ptr())
    }

    pub fn wait_for_finish(&self) {
        unsafe {
            for i in 0..self.threads() {
                fence(Ordering::AcqRel);
                compiler_fence(Ordering::AcqRel);
                let root_moves = self.get_list_unchecked(i).moves;
                (*root_moves).finished.await(true);
            }
        }
    }
    pub fn wait_for_start(&self) {
        unsafe {
            let num_threads = self.threads();
            for i in 0..num_threads {
                fence(Ordering::AcqRel);
                compiler_fence(Ordering::AcqRel);
                let root_moves = self.get_list_unchecked(i).moves;
                (*root_moves).finished.await(false);
            }
        }
    }

    pub fn reset_depths(&self) {
        unsafe {
            for i in 0..self.threads() {
                self.get_list_unchecked(i).set_depth_completed(0);
            }
        }
    }

    pub fn thread_best_move_and_depth(&self, thread_id: usize) -> (RootMove, u16) {
        unsafe {
            let mut thread = self.get_list_unchecked(thread_id);
            (thread.first().clone(), thread.depth_completed())
        }

    }

    pub fn best_rootmove(&self, use_stdout: bool) -> RootMove {
        let (mut best_root_move, mut depth_reached): (RootMove, u16) = self.thread_best_move_and_depth(0);
        if use_stdout {
            println!("id: 0, value: {}, prev_value: {}, depth: {}, depth_comp: {}, mov: {}", best_root_move.score, best_root_move.prev_score, best_root_move.depth_reached,depth_reached, best_root_move.bit_move);
        }

        for x in 1..self.threads() {
            let (thread_move, thread_depth): (RootMove, u16)  = self.thread_best_move_and_depth(x);
            let depth_diff = thread_depth as i32 - depth_reached as i32;
            let value_diff = thread_move.score - best_root_move.score;
            if x != 0 {
                // If it has a bigger value and greater or equal depth
                if value_diff > 0 && depth_diff >= 0 {
                    best_root_move = thread_move;
                    depth_reached = thread_depth;
                }
            }

            if use_stdout {
                println!("id: {}, value: {}, prev_value: {}, depth: {}, depth_comp: {}, mov: {}",x, thread_move.score, thread_move.prev_score, thread_move.depth_reached,thread_depth, thread_move.bit_move);
            }
        }
        best_root_move
    }
}

pub struct RootMovesIter<'a> {
    root_moves: &'a RmManager,
    threads: usize,
    idx: usize,
}

impl<'a> Iterator for RootMovesIter<'a> {
    type Item = RootMoveList;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.idx >= self.threads {
            None
        } else {
            unsafe {
                Some(self.root_moves.get_list_unchecked(self.idx))
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.threads - self.idx, Some(self.threads - self.idx))
    }
}

impl<'a> IntoIterator for &'a RmManager {
    type Item = RootMoveList;
    type IntoIter = RootMovesIter<'a>;

    #[inline]
    fn into_iter(self) -> Self::IntoIter {
        RootMovesIter {
            root_moves: &self,
            threads: self.threads(),
            idx: 0,
        }
    }
}

impl Drop for RmManager {
    fn drop(&mut self) {
        let num = self.ref_count.fetch_sub(1, Ordering::SeqCst);
        if num == 1 {
            unsafe {
                Heap.dealloc(self.as_raw_ptr() as *mut _,
                             Layout::array::<RawRootMoveList>(MAX_THREADS).unwrap());
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn rm_basic() {
        let mut rms = RmManager::new();
        assert_eq!(rms.threads(), 0);
        let moves_1 = rms.add_thread().unwrap();
        assert_eq!(rms.threads(), 1);
        assert_eq!(moves_1.len(), 0);
        let board = Board::default();
        unsafe {
            rms.replace_moves(&board);
            let moves_1_clone = rms.get_list_unchecked(0);
            assert_eq!(moves_1.len(), moves_1_clone.len());
            let moves_2 = rms.add_thread().unwrap();
            assert_eq!(rms.threads(), 2);
            rms.replace_moves(&board);
            let moves_2_clone = rms.get_list_unchecked(0);
            assert_eq!(moves_2.len(), moves_2_clone.len());
            assert_eq!(moves_1.len(), moves_2_clone.len());
        }
    }

    #[test]
    fn rm_cloning() {
        let mut rms = RmManager::new();
        let rmsc = rms.clone();
        rms.add_thread();
        rms.add_thread();
        assert_eq!(rms.threads(), rmsc.threads());
    }
}<commit_msg>sadff<commit_after>use super::RootMove;
use super::super::MAX_THREADS;
use super::root_moves_list::{RootMoveList,RawRootMoveList};

use std::heap::{Alloc, Layout, Heap};
use std::ptr::Unique;
use std::ptr::Shared;
use std::sync::Arc;
use std::sync::atomic::{Ordering,AtomicUsize,fence,compiler_fence};
use std::mem;

use std::iter::{Iterator,IntoIterator};

use pleco::Board;
use pleco::board::movegen::{MoveGen,Legal};
use pleco::core::mono_traits::AllGenType;

pub type RawRmManager = [RawRootMoveList; MAX_THREADS];

pub struct RmManager {
    threads: Arc<AtomicUsize>,
    moves: Shared<RawRmManager>,
    ref_count: Arc<AtomicUsize>
}

unsafe impl Send for RmManager {}

impl Clone for RmManager {
    fn clone(&self) -> Self {
        let ref_count: Arc<AtomicUsize> = self.ref_count.clone();
        ref_count.fetch_add(1, Ordering::SeqCst);
        RmManager {
            threads: self.threads.clone(),
            moves: self.moves.clone(),
            ref_count
        }
    }
}

impl RmManager {
    pub fn new() -> Self {
        let mut rms = RmManager::init();
        rms.allocate();
        unsafe { rms.set_states();}
        rms
    }

    fn init() -> Self {
        RmManager {
            threads: Arc::new(AtomicUsize::new(0)),
            moves: Shared::empty(),
            ref_count: Arc::new(AtomicUsize::new(1))
        }
    }

    fn allocate(&mut self) {
        unsafe {
            let elem_size = mem::size_of::<RawRootMoveList>();
            let alloc_size = elem_size * MAX_THREADS;
            let align = mem::align_of::<RawRootMoveList>();
            let layout = Layout::from_size_align(alloc_size, align).unwrap();
            let result = Heap.alloc_zeroed(layout);
            let new_ptr = match result {
                Ok(ptr) => ptr,
                Err(err) => Heap.oom(err),
            };
            {
                println!("elem size: {}, align: {}, alloc size: {}",elem_size,align, alloc_size);
                let location: usize = mem::transmute::<*mut u8, usize>(new_ptr);
                println!("Bytes at {:x}", location);
            }
            self.moves = Shared::new(new_ptr as *mut RawRmManager).unwrap();
        }
    }

    unsafe fn set_states(&mut self) {
        for x in 0..MAX_THREADS {
            let line =  self.as_raw_ptr().offset(x as isize);
            let location: usize = mem::transmute::<*mut RawRootMoveList, usize>(line);
            println!("This x at {:x}", location);
            let mut raw_list = self.get_list_unchecked(x);
            raw_list.init();
        }
    }

    pub fn threads(&self) -> usize {
        self.threads.load(Ordering::Relaxed)
    }

    pub fn add_thread(&mut self) -> Option<RootMoveList> {
        if self.threads() >= MAX_THREADS {
            None
        } else {
            let thread_idx = self.threads.fetch_add(1, Ordering::SeqCst);
            unsafe {
                Some(self.get_list_unchecked(thread_idx))
            }
        }
    }

    pub fn get_list(&self, num: usize) -> Option<RootMoveList> {
        if num >= self.threads() {
            None
        } else {
            unsafe {
                Some(self.get_list_unchecked(num))
            }
        }
    }

    pub unsafe fn get_list_unchecked(&self, num: usize) -> RootMoveList {
        RootMoveList {
            moves: self.as_raw_ptr().offset(num as isize)
        }
    }

    pub unsafe fn replace_moves(&mut self, board: &Board) {
        let legal_moves = MoveGen::generate::<Legal, AllGenType>(&board);
        let mut first = self.as_ptr();
        first.replace(&legal_moves);
        let num = self.threads();
        for i in 1..num {
            self.get_list_unchecked(i).clone_from_other(&first);
        }
    }

    pub unsafe fn as_ptr(&self) -> RootMoveList {
        RootMoveList {
            moves: mem::transmute::<*mut RawRmManager, *mut RawRootMoveList>(self.moves.as_ptr())
        }
    }

    unsafe fn as_raw_ptr(&self) -> *mut RawRootMoveList {
        mem::transmute::<*mut RawRmManager, *mut RawRootMoveList>(self.moves.as_ptr())
    }

    pub fn wait_for_finish(&self) {
        unsafe {
            for i in 0..self.threads() {
                fence(Ordering::AcqRel);
                compiler_fence(Ordering::AcqRel);
                let root_moves = self.get_list_unchecked(i).moves;
                (*root_moves).finished.await(true);
            }
        }
    }
    pub fn wait_for_start(&self) {
        unsafe {
            let num_threads = self.threads();
            for i in 0..num_threads {
                fence(Ordering::AcqRel);
                compiler_fence(Ordering::AcqRel);
                let root_moves = self.get_list_unchecked(i).moves;
                (*root_moves).finished.await(false);
            }
        }
    }

    pub fn reset_depths(&self) {
        unsafe {
            for i in 0..self.threads() {
                self.get_list_unchecked(i).set_depth_completed(0);
            }
        }
    }

    pub fn thread_best_move_and_depth(&self, thread_id: usize) -> (RootMove, u16) {
        unsafe {
            let mut thread = self.get_list_unchecked(thread_id);
            (thread.first().clone(), thread.depth_completed())
        }

    }

    pub fn best_rootmove(&self, use_stdout: bool) -> RootMove {
        let (mut best_root_move, mut depth_reached): (RootMove, u16) = self.thread_best_move_and_depth(0);
        if use_stdout {
            println!("id: 0, value: {}, prev_value: {}, depth: {}, depth_comp: {}, mov: {}", best_root_move.score, best_root_move.prev_score, best_root_move.depth_reached,depth_reached, best_root_move.bit_move);
        }

        for x in 1..self.threads() {
            let (thread_move, thread_depth): (RootMove, u16)  = self.thread_best_move_and_depth(x);
            let depth_diff = thread_depth as i32 - depth_reached as i32;
            let value_diff = thread_move.score - best_root_move.score;
            if x != 0 {
                // If it has a bigger value and greater or equal depth
                if value_diff > 0 && depth_diff >= 0 {
                    best_root_move = thread_move;
                    depth_reached = thread_depth;
                }
            }

            if use_stdout {
                println!("id: {}, value: {}, prev_value: {}, depth: {}, depth_comp: {}, mov: {}",x, thread_move.score, thread_move.prev_score, thread_move.depth_reached,thread_depth, thread_move.bit_move);
            }
        }
        best_root_move
    }
}

pub struct RootMovesIter<'a> {
    root_moves: &'a RmManager,
    threads: usize,
    idx: usize,
}

impl<'a> Iterator for RootMovesIter<'a> {
    type Item = RootMoveList;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.idx >= self.threads {
            None
        } else {
            unsafe {
                Some(self.root_moves.get_list_unchecked(self.idx))
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.threads - self.idx, Some(self.threads - self.idx))
    }
}

impl<'a> IntoIterator for &'a RmManager {
    type Item = RootMoveList;
    type IntoIter = RootMovesIter<'a>;

    #[inline]
    fn into_iter(self) -> Self::IntoIter {
        RootMovesIter {
            root_moves: &self,
            threads: self.threads(),
            idx: 0,
        }
    }
}

impl Drop for RmManager {
    fn drop(&mut self) {
        let num = self.ref_count.fetch_sub(1, Ordering::SeqCst);
        if num == 1 {
            unsafe {
                Heap.dealloc(self.as_raw_ptr() as *mut _,
                             Layout::array::<RawRootMoveList>(MAX_THREADS).unwrap());
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn rm_basic() {
        let mut rms = RmManager::new();
        assert_eq!(rms.threads(), 0);
        let moves_1 = rms.add_thread().unwrap();
        assert_eq!(rms.threads(), 1);
        assert_eq!(moves_1.len(), 0);
        let board = Board::default();
        unsafe {
            rms.replace_moves(&board);
            let moves_1_clone = rms.get_list_unchecked(0);
            assert_eq!(moves_1.len(), moves_1_clone.len());
            let moves_2 = rms.add_thread().unwrap();
            assert_eq!(rms.threads(), 2);
            rms.replace_moves(&board);
            let moves_2_clone = rms.get_list_unchecked(0);
            assert_eq!(moves_2.len(), moves_2_clone.len());
            assert_eq!(moves_1.len(), moves_2_clone.len());
        }
    }

    #[test]
    fn rm_cloning() {
        let mut rms = RmManager::new();
        let rmsc = rms.clone();
        rms.add_thread();
        rms.add_thread();
        assert_eq!(rms.threads(), rmsc.threads());
    }
}<|endoftext|>


<commit_before>//! Shadowsocks TCP transparent proxy

use std::{io, net::SocketAddr, sync::Arc, time::Duration};

use log::{debug, error, info, trace};
use shadowsocks::{lookup_then, relay::socks5::Address};
use tokio::{
    net::{TcpListener, TcpStream},
    time,
};

use crate::{
    config::{ClientConfig, RedirType},
    local::{
        context::ServiceContext,
        loadbalancing::{PingBalancer, ServerIdent},
        net::AutoProxyClientStream,
        redir::redir_ext::{TcpListenerRedirExt, TcpStreamRedirExt},
        utils::establish_tcp_tunnel,
    },
};

mod sys;

/// Established Client Transparent Proxy
///
/// This method must be called after handshaking with client (for example, socks5 handshaking)
async fn establish_client_tcp_redir<'a>(
    context: Arc<ServiceContext>,
    server: &ServerIdent,
    mut stream: TcpStream,
    peer_addr: SocketAddr,
    addr: &Address,
    nodelay: bool,
) -> io::Result<()> {
    let svr_cfg = server.server_config();
    trace!(
        "establishing tcp redir tunnel {} <-> {} through sever {} (outbound: {})",
        peer_addr,
        addr,
        svr_cfg.external_addr(),
        svr_cfg.addr(),
    );

    let remote = AutoProxyClientStream::connect_proxied(context, &server, addr).await?;

    if nodelay {
        remote.set_nodelay(true)?;
    }

    let (mut plain_reader, mut plain_writer) = stream.split();
    let (mut shadow_reader, mut shadow_writer) = remote.into_split();

    establish_tcp_tunnel(
        svr_cfg,
        &mut plain_reader,
        &mut plain_writer,
        &mut shadow_reader,
        &mut shadow_writer,
        peer_addr,
        addr,
    )
    .await
}

async fn handle_redir_client(
    context: Arc<ServiceContext>,
    server: &ServerIdent,
    s: TcpStream,
    peer_addr: SocketAddr,
    daddr: SocketAddr,
    nodelay: bool,
) -> io::Result<()> {
    // let svr_cfg = server.server_config();
    //
    // if let Err(err) = s.set_keepalive(svr_cfg.timeout()) {
    //     error!("failed to set keep alive: {:?}", err);
    // }

    if nodelay {
        if let Err(err) = s.set_nodelay(true) {
            error!("failed to set TCP_NODELAY on accepted socket, error: {:?}", err);
        }
    }

    // Get forward address from socket
    let target_addr = Address::from(daddr);
    establish_client_tcp_redir(context, server, s, peer_addr, &target_addr, nodelay).await
}

pub async fn run_tcp_redir(
    context: Arc<ServiceContext>,
    client_config: &ClientConfig,
    balancer: PingBalancer,
    redir_ty: RedirType,
    nodelay: bool,
) -> io::Result<()> {
    let listener = match *client_config {
        ClientConfig::SocketAddr(ref saddr) => TcpListener::bind_redir(redir_ty, *saddr).await?,
        ClientConfig::DomainName(ref dname, port) => {
            lookup_then!(context.context_ref(), dname, port, |addr| {
                TcpListener::bind_redir(redir_ty, addr).await
            })?
            .1
        }
    };

    let actual_local_addr = listener.local_addr().expect("determine port bound to");

    info!("shadowsocks TCP redirect listening on {}", actual_local_addr);

    loop {
        let (socket, peer_addr) = match listener.accept().await {
            Ok(s) => s,
            Err(err) => {
                error!("accept failed with error: {}", err);
                time::sleep(Duration::from_secs(1)).await;
                continue;
            }
        };
        let server = balancer.best_tcp_server();

        trace!("got connection {}", peer_addr);
        trace!("picked proxy server: {:?}", server.server_config());

        let context = context.clone();
        tokio::spawn(async move {
            let dst_addr = match socket.destination_addr(redir_ty) {
                Ok(d) => d,
                Err(err) => {
                    error!(
                        "TCP redirect couldn't get destination, peer: {}, error: {}",
                        peer_addr, err
                    );
                    return;
                }
            };

            if let Err(err) = handle_redir_client(context, &server, socket, peer_addr, dst_addr, nodelay).await {
                debug!("TCP redirect client, error: {:?}", err);
            }
        });
    }
}
<commit_msg>redir tcp should connect remote based on acl<commit_after>//! Shadowsocks TCP transparent proxy

use std::{io, net::SocketAddr, sync::Arc, time::Duration};

use log::{debug, error, info, trace};
use shadowsocks::{lookup_then, relay::socks5::Address};
use tokio::{
    net::{TcpListener, TcpStream},
    time,
};

use crate::{
    config::{ClientConfig, RedirType},
    local::{
        context::ServiceContext,
        loadbalancing::{PingBalancer, ServerIdent},
        net::AutoProxyClientStream,
        redir::redir_ext::{TcpListenerRedirExt, TcpStreamRedirExt},
        utils::establish_tcp_tunnel,
    },
};

mod sys;

/// Established Client Transparent Proxy
///
/// This method must be called after handshaking with client (for example, socks5 handshaking)
async fn establish_client_tcp_redir<'a>(
    context: Arc<ServiceContext>,
    server: &ServerIdent,
    mut stream: TcpStream,
    peer_addr: SocketAddr,
    addr: &Address,
    nodelay: bool,
) -> io::Result<()> {
    let svr_cfg = server.server_config();
    trace!(
        "establishing tcp redir tunnel {} <-> {} through sever {} (outbound: {})",
        peer_addr,
        addr,
        svr_cfg.external_addr(),
        svr_cfg.addr(),
    );

    let remote = AutoProxyClientStream::connect(context, &server, addr).await?;

    if nodelay {
        remote.set_nodelay(true)?;
    }

    let (mut plain_reader, mut plain_writer) = stream.split();
    let (mut shadow_reader, mut shadow_writer) = remote.into_split();

    establish_tcp_tunnel(
        svr_cfg,
        &mut plain_reader,
        &mut plain_writer,
        &mut shadow_reader,
        &mut shadow_writer,
        peer_addr,
        addr,
    )
    .await
}

async fn handle_redir_client(
    context: Arc<ServiceContext>,
    server: &ServerIdent,
    s: TcpStream,
    peer_addr: SocketAddr,
    daddr: SocketAddr,
    nodelay: bool,
) -> io::Result<()> {
    // let svr_cfg = server.server_config();
    //
    // if let Err(err) = s.set_keepalive(svr_cfg.timeout()) {
    //     error!("failed to set keep alive: {:?}", err);
    // }

    if nodelay {
        if let Err(err) = s.set_nodelay(true) {
            error!("failed to set TCP_NODELAY on accepted socket, error: {:?}", err);
        }
    }

    // Get forward address from socket
    let target_addr = Address::from(daddr);
    establish_client_tcp_redir(context, server, s, peer_addr, &target_addr, nodelay).await
}

pub async fn run_tcp_redir(
    context: Arc<ServiceContext>,
    client_config: &ClientConfig,
    balancer: PingBalancer,
    redir_ty: RedirType,
    nodelay: bool,
) -> io::Result<()> {
    let listener = match *client_config {
        ClientConfig::SocketAddr(ref saddr) => TcpListener::bind_redir(redir_ty, *saddr).await?,
        ClientConfig::DomainName(ref dname, port) => {
            lookup_then!(context.context_ref(), dname, port, |addr| {
                TcpListener::bind_redir(redir_ty, addr).await
            })?
            .1
        }
    };

    let actual_local_addr = listener.local_addr().expect("determine port bound to");

    info!("shadowsocks TCP redirect listening on {}", actual_local_addr);

    loop {
        let (socket, peer_addr) = match listener.accept().await {
            Ok(s) => s,
            Err(err) => {
                error!("accept failed with error: {}", err);
                time::sleep(Duration::from_secs(1)).await;
                continue;
            }
        };
        let server = balancer.best_tcp_server();

        trace!("got connection {}", peer_addr);
        trace!("picked proxy server: {:?}", server.server_config());

        let context = context.clone();
        tokio::spawn(async move {
            let dst_addr = match socket.destination_addr(redir_ty) {
                Ok(d) => d,
                Err(err) => {
                    error!(
                        "TCP redirect couldn't get destination, peer: {}, error: {}",
                        peer_addr, err
                    );
                    return;
                }
            };

            if let Err(err) = handle_redir_client(context, &server, socket, peer_addr, dst_addr, nodelay).await {
                debug!("TCP redirect client, error: {:?}", err);
            }
        });
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! UTF-8 encoded text.

use std::{convert, str, ops, ptr};

use {Error, Result};

#[derive(Copy, Clone)]
pub struct Owned(());

impl<'a> ::traits::Owned<'a> for Owned {
    type Reader = Reader<'a>;
    type Builder = Builder<'a>;
}

pub type Reader<'a> = &'a str;

pub fn new_reader<'a>(v : &'a [u8]) -> Result<Reader<'a>> {
    match str::from_utf8(v) {
        Ok(v) => Ok(v),
        Err(e) => Err(Error::failed(
            format!("Text contains non-utf8 data: {:?}", e))),
    }
}

impl <'a> ::traits::FromPointerReader<'a> for Reader<'a> {
    fn get_from_pointer(reader : &::private::layout::PointerReader<'a>) -> Result<Reader<'a>> {
        reader.get_text(ptr::null(), 0)
    }
}

pub struct Builder<'a> {
    bytes: &'a mut [u8],
    pos: usize,
}

impl <'a> Builder <'a> {
    pub fn new<'b>(bytes: &'b mut [u8], pos: u32) -> Result<Builder<'b>> {
        if pos != 0 {
            if let Err(e) = str::from_utf8(bytes) {
                return Err(Error::failed(
                    format!("Text contains non-utf8 data: {:?}", e)))
            }
        }
        Ok(Builder { bytes: bytes, pos: pos as usize })
    }

    pub fn push_ascii(&mut self, ascii: u8) {
        assert!(ascii < 128);
        self.bytes[self.pos] = ascii;
        self.pos += 1;
    }

    pub fn push_str(&mut self, string: &str) {
        let bytes = string.as_bytes();
        for ii in 0..bytes.len() {
            self.bytes[self.pos + ii] = bytes[ii];
        }
        self.pos += bytes.len();
    }

    pub fn clear(&mut self) {
        for ii in 0..self.pos {
            self.bytes[ii] = 0;
        }
        self.pos = 0;
    }
}

impl <'a> ops::Deref for Builder <'a> {
    type Target = str;
    fn deref<'b>(&'b self) -> &'b str {
        str::from_utf8(self.bytes)
            .expect("text::Builder contents are checked for utf8-validity upon construction")
    }
}

impl <'a> convert::AsRef<str> for Builder<'a> {
    fn as_ref<'b>(&'b self) -> &'b str {
        str::from_utf8(self.bytes)
            .expect("text::Builder contents are checked for utf8-validity upon construction")
    }
}

impl <'a> ::traits::FromPointerBuilder<'a> for Builder<'a> {
    fn init_pointer(builder: ::private::layout::PointerBuilder<'a>, size: u32) -> Builder<'a> {
        builder.init_text(size)
    }
    fn get_from_pointer(builder: ::private::layout::PointerBuilder<'a>) -> Result<Builder<'a>> {
        builder.get_text(::std::ptr::null(), 0)
    }
}

impl <'a> ::traits::SetPointerBuilder<Builder<'a>> for Reader<'a> {
    fn set_pointer_builder<'b>(pointer: ::private::layout::PointerBuilder<'b>,
                               value: Reader<'a>,
                               _canonicalize: bool)
                               -> Result<()>
    {
        pointer.set_text(value);
        Ok(())
    }
}
<commit_msg>implement DerefMut for text::Builder<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! UTF-8 encoded text.

use std::{convert, str, ops, ptr};

use {Error, Result};

#[derive(Copy, Clone)]
pub struct Owned(());

impl<'a> ::traits::Owned<'a> for Owned {
    type Reader = Reader<'a>;
    type Builder = Builder<'a>;
}

pub type Reader<'a> = &'a str;

pub fn new_reader<'a>(v : &'a [u8]) -> Result<Reader<'a>> {
    match str::from_utf8(v) {
        Ok(v) => Ok(v),
        Err(e) => Err(Error::failed(
            format!("Text contains non-utf8 data: {:?}", e))),
    }
}

impl <'a> ::traits::FromPointerReader<'a> for Reader<'a> {
    fn get_from_pointer(reader : &::private::layout::PointerReader<'a>) -> Result<Reader<'a>> {
        reader.get_text(ptr::null(), 0)
    }
}

pub struct Builder<'a> {
    bytes: &'a mut [u8],
    pos: usize,
}

impl <'a> Builder <'a> {
    pub fn new<'b>(bytes: &'b mut [u8], pos: u32) -> Result<Builder<'b>> {
        if pos != 0 {
            if let Err(e) = str::from_utf8(bytes) {
                return Err(Error::failed(
                    format!("Text contains non-utf8 data: {:?}", e)))
            }
        }
        Ok(Builder { bytes: bytes, pos: pos as usize })
    }

    pub fn push_ascii(&mut self, ascii: u8) {
        assert!(ascii < 128);
        self.bytes[self.pos] = ascii;
        self.pos += 1;
    }

    pub fn push_str(&mut self, string: &str) {
        let bytes = string.as_bytes();
        for ii in 0..bytes.len() {
            self.bytes[self.pos + ii] = bytes[ii];
        }
        self.pos += bytes.len();
    }

    pub fn clear(&mut self) {
        for ii in 0..self.pos {
            self.bytes[ii] = 0;
        }
        self.pos = 0;
    }
}

impl <'a> ops::Deref for Builder <'a> {
    type Target = str;
    fn deref<'b>(&'b self) -> &'b str {
        str::from_utf8(self.bytes)
            .expect("text::Builder contents are checked for utf8-validity upon construction")
    }
}

impl <'a> ops::DerefMut for Builder <'a> {
    fn deref_mut<'b>(&'b mut self) -> &'b mut str {
        str::from_utf8_mut(self.bytes)
            .expect("text::Builder contents are checked for utf8-validity upon construction")
    }
}

impl <'a> convert::AsRef<str> for Builder<'a> {
    fn as_ref<'b>(&'b self) -> &'b str {
        str::from_utf8(self.bytes)
            .expect("text::Builder contents are checked for utf8-validity upon construction")
    }
}

impl <'a> ::traits::FromPointerBuilder<'a> for Builder<'a> {
    fn init_pointer(builder: ::private::layout::PointerBuilder<'a>, size: u32) -> Builder<'a> {
        builder.init_text(size)
    }
    fn get_from_pointer(builder: ::private::layout::PointerBuilder<'a>) -> Result<Builder<'a>> {
        builder.get_text(::std::ptr::null(), 0)
    }
}

impl <'a> ::traits::SetPointerBuilder<Builder<'a>> for Reader<'a> {
    fn set_pointer_builder<'b>(pointer: ::private::layout::PointerBuilder<'b>,
                               value: Reader<'a>,
                               _canonicalize: bool)
                               -> Result<()>
    {
        pointer.set_text(value);
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate htmlstream;
extern crate reqwest;
use story_builder::article_provider::*;
use std::io::Read;
use self::htmlstream::HTMLTagState;

struct HTTPArticle {
    paragraphs: Vec<Paragraph>,
    topic: String,
}

impl Article for HTTPArticle {
    fn get_paragraphs(&self) -> &Vec<Paragraph> {
        &self.paragraphs
    }
    fn get_topic(&self) -> &str {
        &self.topic
    }
}

pub struct HTTPArticleProvider {
    base_uri_for_get: &'static str,
    base_uri_for_search: &'static str,
}

impl HTTPArticleProvider {
    pub fn new() -> HTTPArticleProvider {
        HTTPArticleProvider {
            base_uri_for_get: "https://en.wikipedia.org/wiki/",
            base_uri_for_search: "https://en.wikipedia.org/w/index.php?title=Special:Search&fulltext=1&search=",
        }
    }
    /// This function takes a `topic` and replaces all spaces with underscores
    fn to_wiki_str(topic: &str) -> String {
        topic.replace(" ", "_")
    }

    /// Parse the body of the HTML search results page and extract all topics found in it.
    fn extract_results_from_search(body: &str) -> Vec<String> {
        let mut results: Vec<String> = vec![];
        // iterate over each tag in the body
        for (_, tag) in htmlstream::tag_iter(body) {
            // if we find an opening  tag:
            if (tag.name == "a") && (tag.state == HTMLTagState::Opening) {
                // Extract the title and href of the tag:
                let mut title: Option<String> = None;
                let mut href: Option<String> = None;
                let mut has_data_serp_pos: bool = false;
                for (_, attr) in htmlstream::attr_iter(&tag.attributes) {
                    match attr.name.as_str() {
                        "title" => title = Some(attr.value),
                        "href" => href = Some(attr.value),
                        "data-serp-pos" => has_data_serp_pos = true,
                        _ => (),
                    };
                }
                // check if the  was valid:
                if has_data_serp_pos {
                    match (title, href) {
                        (Some(title), Some(href)) => if &href[..6] == "/wiki/" {
                            results.push(title);
                        },
                        _ => (),
                    };
                }
            }
        }
        results
    }

    /// Parse the body of the HTML article page and extract all paragraphs along with
    /// the topics found in them.
    fn extract_paragraphs_from_body(body: &str) -> Vec<Paragraph> {
        if body == "" {
            return vec![];
        }
        #[derive(Copy, Clone)]
        #[allow(non_camel_case_types)]
        enum State {
            SEARCH_FOR_P,
            READING_P,
            SKIPPING_SUP,
        }
        let mut state = State::SEARCH_FOR_P;
        let mut paragraphs: Vec<Paragraph> = vec![];
        let mut current_par: Option<Paragraph> = None;

        for (_, tag) in htmlstream::tag_iter(body) {
            match (state, tag.name.as_str()) {
                // If we are in state SEARCH_FOR_P and find a p:
                (State::SEARCH_FOR_P, "p") => {
                    // Create a new empty Paragraph:
                    current_par = Some(Paragraph {
                        text: String::from(""),
                        topics: vec![],
                    });
                    state = State::READING_P; // Switch to READING_P state.
                }
                // IF we are in READING_P state and finds text outside of a tag, append it to the current paragraph:
                (State::READING_P, "") => {
                    current_par.as_mut().unwrap().text.push_str(&tag.html);
                }
                // IF we are in READING_P state and finds an  tag, append it to the topic list of this paragraph:
                (State::READING_P, "a") => {
                    // Try to find the title of the reference:
                    for (_, attr) in htmlstream::attr_iter(&tag.attributes) {
                        if (attr.name == "title") && (!attr.value.contains(":")) {
                            &current_par.as_mut().unwrap().topics.push(attr.value);
                        }
                    }
                }
                // IF we are in READING_P state and finds a
 tag, then it is the end of the
;
                // Push it in the final vector and return to the initial state.
                (State::READING_P, "p") => {
                    let par = current_par.take().unwrap();
                    paragraphs.push(par);

                    state = State::SEARCH_FOR_P;
                }
                (State::READING_P, "sup") => {
                    // Enter SKIPPING_SUP state; we skip all tags until we found the corresponding
                    state = State::SKIPPING_SUP;
                }
                (State::SKIPPING_SUP, "sup") => {
                    // We are done skipping; back to READING_P
                    state = State::READING_P;
                }
                _ => (),
            };
        }
        paragraphs // Return the vector we built.
    }
}

impl ArticleProvider for HTTPArticleProvider {
    fn get(&self, topic: &str) -> Option<Box
> {
        if topic == "" {
            return None; // Do not even try if the topic is empty.
        }
        let mut uri = String::from(self.base_uri_for_get);
        uri.push_str(&HTTPArticleProvider::to_wiki_str(topic));
        let mut resp = reqwest::get(&uri).unwrap();
        // IF for whatever reason we do not get an OK from wikipedia,
        // consider as "Not found" and return None.
        if resp.status() != reqwest::StatusCode::Ok {
            return None;
        }

        let mut content = String::new();
        resp.read_to_string(&mut content)
            .expect("Could not read content from HTTP response.");

        Some(Box::new(HTTPArticle {
            paragraphs: HTTPArticleProvider::extract_paragraphs_from_body(&content),
            topic: topic.to_owned(),
        }))
    }

    fn search(&self, topic: &str) -> Vec<String> {
        let mut uri = String::from(self.base_uri_for_search);
        uri.push_str(&HTTPArticleProvider::to_wiki_str(topic));
        let mut resp = reqwest::get(&uri).unwrap();
        assert!(resp.status().is_success());
        let mut content = String::new();
        resp.read_to_string(&mut content)
            .expect("Could not read content from HTTP response.");
        HTTPArticleProvider::extract_results_from_search(&content)
    }
}

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

    #[test]
    fn to_wiki_str_is_working() {
        assert_eq!(HTTPArticleProvider::to_wiki_str(""), "");
        assert_eq!(HTTPArticleProvider::to_wiki_str(" test"), "_test");
        assert_eq!(HTTPArticleProvider::to_wiki_str("test test"), "test_test");
        assert_eq!(HTTPArticleProvider::to_wiki_str("test_test"), "test_test");
        assert_eq!(HTTPArticleProvider::to_wiki_str("  __  __"), "________");
    }
    #[test]
    // This test actually fetches search results from Wikipedia. It may FAIL due to changes in the search
    // results themselves rather than a code issue. FIXME test on locally hosted version of wikipedia
    fn search_results_works() {
        let provider = HTTPArticleProvider::new();
        let mut first_3_results = provider.search("test1234");
        first_3_results.resize(3, "".to_owned());
        let search_results_for_test1234 = vec![
            "German submarine U-1234".to_owned(),
            "2,3,3,3-Tetrafluoropropene".to_owned(),
            "Unit testing".to_owned(),
        ];
        assert_eq!(first_3_results, search_results_for_test1234);
    }

    #[test]
    fn extract_results_from_empty_search() {
        let empty_vec: Vec<String> = vec![];
        assert_eq!(
            HTTPArticleProvider::extract_results_from_search(""),
            empty_vec
        );
    }

    #[test]
    fn extract_results_from_test1234() {
        let empty_vec: Vec<String> = vec![];
        let search_results_for_test1234 = vec![
            "German submarine U-1234".to_owned(),
            "2,3,3,3-Tetrafluoropropene".to_owned(),
            "Unit testing".to_owned(),
        ];
        assert_eq!(
            HTTPArticleProvider::extract_results_from_search(
                "junkblablabla\

\

\

\
                 German submarine U-1234\
                     \

\

German submarine U-1234 was a Type IXC/40 U-boat of Nazi Germany's Kriegsmarine built during World War II for service in the Battle of the Atlantic. U-1234\

 \

9 KB (893 words) - 19:19, 17 June 2017\

\

\

\

\
                 2,3,3,3-Tetrafluoropropene\
                     \

\

2,3,3,3-Tetrafluoropropene, or HFO-1234yf, is a hydrofluoroolefin (HFO) with the formula CH2=CFCF3. This colorless gas has been proposed as a replacement\

 \

12 KB (1,291 words) - 17:02, 26 June 2017\

\

\

\

\
                 Unit testing\
                     \

\

In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules\

 \

28 KB (3,455 words) - 18:46, 5 August 2017\

"
            ),
            search_results_for_test1234
        );
    }

    #[test]
    fn get_from_empty_returns_none() {
        let provider = HTTPArticleProvider::new();
        match provider.get("") {
            None => (),
            _ => panic!("Expected None, got Some."),
        }
    }

    #[test]
    fn get_from_non_existing_returns_none() {
        let provider = HTTPArticleProvider::new();
        match provider.get("fsdafgnhtyunjfthdhtydfrt67yh65dgdtydtvydrgdrt") {
            None => (),
            _ => panic!("Expected None, got Some."),
        }
    }

    #[test]
    fn test_get_html_parser_empty_body_should_return_empty_vec() {
        assert!(HTTPArticleProvider::extract_results_from_search("").len() == 0);
    }

    #[test]
    fn test_get_html_parser_with_basic_p_format() {
        let mut body = String::new();
        body.push_str("junkhere......");
        body.push_str("
");
        body.push_str("Lynx's most notable ");
        body.push_str("deep sky object");
        body.push_str(" is ");
        body.push_str("NGC 2419");
        body.push_str(", also called the \"Intergalactic Wanderer\" as it was assumed to lie outside the Milky Way. At a distance of between 275,000 and 300,000 light-years from Earth, it is one of the most distant known ");
        body.push_str("globular clusters");
        body.push_str(" within our galaxy. NGC 2419 is likely in a highly elliptical orbit around the ");
        body.push_str("Milky Way");
        body.push_str(".");
        body.push_str("");
        body.push_str("[30]");
        body.push_str("");
        body.push_str(" It has a magnitude of 10.3 and is a ");
        body.push_str("Shapley class II");
        body.push_str(" cluster; this classification indicates that it is extremely concentrated at its center. Originally thought to be a star, NGC 2419 was discovered to be a globular cluster by American astronomer ");
        body.push_str("Carl Lampland");
        body.push_str(".");
        body.push_str("");
        body.push_str("[31]");
        body.push_str("");
        body.push_str("
");
        body.push_str("junkafter...");
        let result = HTTPArticleProvider::extract_paragraphs_from_body(&body);
        assert!(result.len() == 1);
        assert_eq!(
            result[0].text,
            "Lynx's most notable deep sky object is NGC 2419, also called the \"Intergalactic Wanderer\" as it was assumed to lie outside the Milky Way. At a distance of between 275,000 and 300,000 light-years from Earth, it is one of the most distant known globular clusters within our galaxy. NGC 2419 is likely in a highly elliptical orbit around the Milky Way. It has a magnitude of 10.3 and is a Shapley class II cluster; this classification indicates that it is extremely concentrated at its center. Originally thought to be a star, NGC 2419 was discovered to be a globular cluster by American astronomer Carl Lampland."
        );
    }
}
<commit_msg>Only test if "search" return  results since actual results keeps changing and breaking tests.<commit_after>extern crate htmlstream;
extern crate reqwest;
use story_builder::article_provider::*;
use std::io::Read;
use self::htmlstream::HTMLTagState;

struct HTTPArticle {
    paragraphs: Vec<Paragraph>,
    topic: String,
}

impl Article for HTTPArticle {
    fn get_paragraphs(&self) -> &Vec<Paragraph> {
        &self.paragraphs
    }
    fn get_topic(&self) -> &str {
        &self.topic
    }
}

pub struct HTTPArticleProvider {
    base_uri_for_get: &'static str,
    base_uri_for_search: &'static str,
}

impl HTTPArticleProvider {
    pub fn new() -> HTTPArticleProvider {
        HTTPArticleProvider {
            base_uri_for_get: "https://en.wikipedia.org/wiki/",
            base_uri_for_search: "https://en.wikipedia.org/w/index.php?title=Special:Search&fulltext=1&search=",
        }
    }
    /// This function takes a `topic` and replaces all spaces with underscores
    fn to_wiki_str(topic: &str) -> String {
        topic.replace(" ", "_")
    }

    /// Parse the body of the HTML search results page and extract all topics found in it.
    fn extract_results_from_search(body: &str) -> Vec<String> {
        let mut results: Vec<String> = vec![];
        // iterate over each tag in the body
        for (_, tag) in htmlstream::tag_iter(body) {
            // if we find an opening  tag:
            if (tag.name == "a") && (tag.state == HTMLTagState::Opening) {
                // Extract the title and href of the tag:
                let mut title: Option<String> = None;
                let mut href: Option<String> = None;
                let mut has_data_serp_pos: bool = false;
                for (_, attr) in htmlstream::attr_iter(&tag.attributes) {
                    match attr.name.as_str() {
                        "title" => title = Some(attr.value),
                        "href" => href = Some(attr.value),
                        "data-serp-pos" => has_data_serp_pos = true,
                        _ => (),
                    };
                }
                // check if the  was valid:
                if has_data_serp_pos {
                    match (title, href) {
                        (Some(title), Some(href)) => if &href[..6] == "/wiki/" {
                            results.push(title);
                        },
                        _ => (),
                    };
                }
            }
        }
        results
    }

    /// Parse the body of the HTML article page and extract all paragraphs along with
    /// the topics found in them.
    fn extract_paragraphs_from_body(body: &str) -> Vec<Paragraph> {
        if body == "" {
            return vec![];
        }
        #[derive(Copy, Clone)]
        #[allow(non_camel_case_types)]
        enum State {
            SEARCH_FOR_P,
            READING_P,
            SKIPPING_SUP,
        }
        let mut state = State::SEARCH_FOR_P;
        let mut paragraphs: Vec<Paragraph> = vec![];
        let mut current_par: Option<Paragraph> = None;

        for (_, tag) in htmlstream::tag_iter(body) {
            match (state, tag.name.as_str()) {
                // If we are in state SEARCH_FOR_P and find a p:
                (State::SEARCH_FOR_P, "p") => {
                    // Create a new empty Paragraph:
                    current_par = Some(Paragraph {
                        text: String::from(""),
                        topics: vec![],
                    });
                    state = State::READING_P; // Switch to READING_P state.
                }
                // IF we are in READING_P state and finds text outside of a tag, append it to the current paragraph:
                (State::READING_P, "") => {
                    current_par.as_mut().unwrap().text.push_str(&tag.html);
                }
                // IF we are in READING_P state and finds an  tag, append it to the topic list of this paragraph:
                (State::READING_P, "a") => {
                    // Try to find the title of the reference:
                    for (_, attr) in htmlstream::attr_iter(&tag.attributes) {
                        if (attr.name == "title") && (!attr.value.contains(":")) {
                            &current_par.as_mut().unwrap().topics.push(attr.value);
                        }
                    }
                }
                // IF we are in READING_P state and finds a
 tag, then it is the end of the
;
                // Push it in the final vector and return to the initial state.
                (State::READING_P, "p") => {
                    let par = current_par.take().unwrap();
                    paragraphs.push(par);

                    state = State::SEARCH_FOR_P;
                }
                (State::READING_P, "sup") => {
                    // Enter SKIPPING_SUP state; we skip all tags until we found the corresponding
                    state = State::SKIPPING_SUP;
                }
                (State::SKIPPING_SUP, "sup") => {
                    // We are done skipping; back to READING_P
                    state = State::READING_P;
                }
                _ => (),
            };
        }
        paragraphs // Return the vector we built.
    }
}

impl ArticleProvider for HTTPArticleProvider {
    fn get(&self, topic: &str) -> Option<Box
> {
        if topic == "" {
            return None; // Do not even try if the topic is empty.
        }
        let mut uri = String::from(self.base_uri_for_get);
        uri.push_str(&HTTPArticleProvider::to_wiki_str(topic));
        let mut resp = reqwest::get(&uri).unwrap();
        // IF for whatever reason we do not get an OK from wikipedia,
        // consider as "Not found" and return None.
        if resp.status() != reqwest::StatusCode::Ok {
            return None;
        }

        let mut content = String::new();
        resp.read_to_string(&mut content)
            .expect("Could not read content from HTTP response.");

        Some(Box::new(HTTPArticle {
            paragraphs: HTTPArticleProvider::extract_paragraphs_from_body(&content),
            topic: topic.to_owned(),
        }))
    }

    fn search(&self, topic: &str) -> Vec<String> {
        let mut uri = String::from(self.base_uri_for_search);
        uri.push_str(&HTTPArticleProvider::to_wiki_str(topic));
        let mut resp = reqwest::get(&uri).unwrap();
        assert!(resp.status().is_success());
        let mut content = String::new();
        resp.read_to_string(&mut content)
            .expect("Could not read content from HTTP response.");
        HTTPArticleProvider::extract_results_from_search(&content)
    }
}

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

    #[test]
    fn to_wiki_str_is_working() {
        assert_eq!(HTTPArticleProvider::to_wiki_str(""), "");
        assert_eq!(HTTPArticleProvider::to_wiki_str(" test"), "_test");
        assert_eq!(HTTPArticleProvider::to_wiki_str("test test"), "test_test");
        assert_eq!(HTTPArticleProvider::to_wiki_str("test_test"), "test_test");
        assert_eq!(HTTPArticleProvider::to_wiki_str("  __  __"), "________");
    }
    #[test]
    // This test actually fetches search results from Wikipedia. It may FAIL due to changes in the search
    // results themselves rather than a code issue. FIXME test on locally hosted version of wikipedia
    fn search_results_works() {
        let provider = HTTPArticleProvider::new();
        let mut results = provider.search("test1234");
        assert!(results.len() > 0);
    }

    #[test]
    fn extract_results_from_empty_search() {
        let empty_vec: Vec<String> = vec![];
        assert_eq!(
            HTTPArticleProvider::extract_results_from_search(""),
            empty_vec
        );
    }

    #[test]
    fn extract_results_from_test1234() {
        let empty_vec: Vec<String> = vec![];
        let search_results_for_test1234 = vec![
            "German submarine U-1234".to_owned(),
            "2,3,3,3-Tetrafluoropropene".to_owned(),
            "Unit testing".to_owned(),
        ];
        assert_eq!(
            HTTPArticleProvider::extract_results_from_search(
                "junkblablabla\

\

\

\
                 German submarine U-1234\
                     \

\

German submarine U-1234 was a Type IXC/40 U-boat of Nazi Germany's Kriegsmarine built during World War II for service in the Battle of the Atlantic. U-1234\

 \

9 KB (893 words) - 19:19, 17 June 2017\

\

\

\

\
                 2,3,3,3-Tetrafluoropropene\
                     \

\

2,3,3,3-Tetrafluoropropene, or HFO-1234yf, is a hydrofluoroolefin (HFO) with the formula CH2=CFCF3. This colorless gas has been proposed as a replacement\

 \

12 KB (1,291 words) - 17:02, 26 June 2017\

\

\

\

\
                 Unit testing\
                     \

\

In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules\

 \

28 KB (3,455 words) - 18:46, 5 August 2017\

"
            ),
            search_results_for_test1234
        );
    }

    #[test]
    fn get_from_empty_returns_none() {
        let provider = HTTPArticleProvider::new();
        match provider.get("") {
            None => (),
            _ => panic!("Expected None, got Some."),
        }
    }

    #[test]
    fn get_from_non_existing_returns_none() {
        let provider = HTTPArticleProvider::new();
        match provider.get("fsdafgnhtyunjfthdhtydfrt67yh65dgdtydtvydrgdrt") {
            None => (),
            _ => panic!("Expected None, got Some."),
        }
    }

    #[test]
    fn test_get_html_parser_empty_body_should_return_empty_vec() {
        assert!(HTTPArticleProvider::extract_results_from_search("").len() == 0);
    }

    #[test]
    fn test_get_html_parser_with_basic_p_format() {
        let mut body = String::new();
        body.push_str("junkhere......");
        body.push_str("
");
        body.push_str("Lynx's most notable ");
        body.push_str("deep sky object");
        body.push_str(" is ");
        body.push_str("NGC 2419");
        body.push_str(", also called the \"Intergalactic Wanderer\" as it was assumed to lie outside the Milky Way. At a distance of between 275,000 and 300,000 light-years from Earth, it is one of the most distant known ");
        body.push_str("globular clusters");
        body.push_str(" within our galaxy. NGC 2419 is likely in a highly elliptical orbit around the ");
        body.push_str("Milky Way");
        body.push_str(".");
        body.push_str("");
        body.push_str("[30]");
        body.push_str("");
        body.push_str(" It has a magnitude of 10.3 and is a ");
        body.push_str("Shapley class II");
        body.push_str(" cluster; this classification indicates that it is extremely concentrated at its center. Originally thought to be a star, NGC 2419 was discovered to be a globular cluster by American astronomer ");
        body.push_str("Carl Lampland");
        body.push_str(".");
        body.push_str("");
        body.push_str("[31]");
        body.push_str("");
        body.push_str("
");
        body.push_str("junkafter...");
        let result = HTTPArticleProvider::extract_paragraphs_from_body(&body);
        assert!(result.len() == 1);
        assert_eq!(
            result[0].text,
            "Lynx's most notable deep sky object is NGC 2419, also called the \"Intergalactic Wanderer\" as it was assumed to lie outside the Milky Way. At a distance of between 275,000 and 300,000 light-years from Earth, it is one of the most distant known globular clusters within our galaxy. NGC 2419 is likely in a highly elliptical orbit around the Milky Way. It has a magnitude of 10.3 and is a Shapley class II cluster; this classification indicates that it is extremely concentrated at its center. Originally thought to be a star, NGC 2419 was discovered to be a globular cluster by American astronomer Carl Lampland."
        );
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use context::{Context, RustcFlags, Trans, Link, Nothing, Pretty, Analysis, Assemble,
                       LLVMAssemble, LLVMCompileBitcode};
use context::{Command, flags_forbidden_for_cmd};
use rustc::version;
use exit_codes::{BAD_FLAG_CODE};
use rustc::driver::{session};

use usage;

use extra::{getopts};
use std::{result};
use std::hashmap::HashSet;

///  Result of parsing command line arguments
pub struct ParseResult {
    // Command
    command: Command,
    // Args
    args: ~[~str],
    // Parsed command line flags
    context: Context,
    // Path to system root
    sysroot: Option<~str>
}

/// Parses command line arguments of rustpkg.
/// Returns a triplet (command, remaining_args, context)
pub fn parse_args(args: &[~str]) -> Result<ParseResult, int> {
    let opts = ~[ getopts::optflag("no-link"),
                                        getopts::optflag("no-trans"),
                 // n.b. Ignores different --pretty options for now
                                        getopts::optflag("pretty"),
                                        getopts::optflag("parse-only"),
                 getopts::optflag("S"), getopts::optflag("assembly"),
                 getopts::optmulti("c"), getopts::optmulti("cfg"),
                 getopts::optflag("v"), getopts::optflag("version"),
                 getopts::optflag("r"), getopts::optflag("rust-path-hack"),
                                        getopts::optopt("sysroot"),
                                        getopts::optflag("emit-llvm"),
                                        getopts::optopt("linker"),
                                        getopts::optopt("link-args"),
                                        getopts::optopt("opt-level"),
                 getopts::optflag("O"),
                                        getopts::optflag("save-temps"),
                                        getopts::optopt("target"),
                                        getopts::optopt("target-cpu"),
                 getopts::optmulti("Z")                                   ];
    let matches = &match getopts::getopts(args, opts) {
        result::Ok(m) => m,
        result::Err(f) => {
            error!("{}", f.to_err_msg());
            return Err(1);
        }
    };
    let no_link = matches.opt_present("no-link");
    let no_trans = matches.opt_present("no-trans");
    let supplied_sysroot = matches.opt_str("sysroot");
    let generate_asm = matches.opt_present("S") ||
        matches.opt_present("assembly");
    let parse_only = matches.opt_present("parse-only");
    let pretty = matches.opt_present("pretty");
    let emit_llvm = matches.opt_present("emit-llvm");

    if matches.opt_present("v") ||
       matches.opt_present("version") {
        version(args[0]);
        return Err(0);
    }

    let use_rust_path_hack = matches.opt_present("r") ||
                             matches.opt_present("rust-path-hack");

    let linker = matches.opt_str("linker");
    let link_args = matches.opt_str("link-args");
    let cfgs = matches.opt_strs("cfg") + matches.opt_strs("c");
    let mut user_supplied_opt_level = true;
    let opt_level = match matches.opt_str("opt-level") {
        Some(~"0") => session::No,
        Some(~"1") => session::Less,
        Some(~"2") => session::Default,
        Some(~"3") => session::Aggressive,
        _ if matches.opt_present("O") => session::Default,
        _ => {
            user_supplied_opt_level = false;
            session::No
        }
    };

    let save_temps = matches.opt_present("save-temps");
    let target     = matches.opt_str("target");
    let target_cpu = matches.opt_str("target-cpu");
    let experimental_features = {
        let strs = matches.opt_strs("Z");
        if matches.opt_present("Z") {
            Some(strs)
        }
        else {
            None
        }
    };

    let mut args = matches.free.clone();
    args.shift();

    if (args.len() < 1) {
        usage::general();
        return Err(1);
    }

    let rustc_flags = RustcFlags {
        linker: linker,
        link_args: link_args,
        optimization_level: opt_level,
        compile_upto: if no_trans {
            Trans
        } else if no_link {
            Link
        } else if pretty {
            Pretty
        } else if parse_only {
            Analysis
        } else if emit_llvm && generate_asm {
            LLVMAssemble
        } else if generate_asm {
            Assemble
        } else if emit_llvm {
            LLVMCompileBitcode
        } else {
            Nothing
        },
        save_temps: save_temps,
        target: target,
        target_cpu: target_cpu,
        additional_library_paths:
            HashSet::new(), // No way to set this from the rustpkg command line
        experimental_features: experimental_features
    };

    let cmd_opt = args.iter().filter_map( |s| from_str(s.clone())).next();
    let command = match(cmd_opt){
        None => {
            debug!("No legal command. Returning 0");
            usage::general();
            return Err(0);
        }
        Some(cmd) => {
            let bad_option = flags_forbidden_for_cmd(&rustc_flags,
                                                              cfgs,
                                                              cmd,
                                                              user_supplied_opt_level);
            if bad_option {
                usage::usage_for_command(cmd);
                debug!("Bad  option, returning BAD_FLAG_CODE");
                return Err(BAD_FLAG_CODE);
            } else {
                cmd
            }
        }
    };

    // Pop off all flags, plus the command
    let mut remaining_args: ~[~str] = args.iter().skip_while(|&s| {
        let maybe_command: Option<Command> = from_str(*s);
        maybe_command.is_none()
    }).map(|s| s.clone()).collect();
    remaining_args.shift();

    let context = Context{
        rustc_flags: rustc_flags,
        cfgs: cfgs,
        use_rust_path_hack: use_rust_path_hack,
    };
    Ok(ParseResult {
        command:  command,
        args: remaining_args,
        context: context,
        sysroot: supplied_sysroot
    })
}

<commit_msg>auto merge of #11466 : eminence/rust/fix_rustpkg_help, r=brson<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use context::{Context, RustcFlags, Trans, Link, Nothing, Pretty, Analysis, Assemble,
                       LLVMAssemble, LLVMCompileBitcode};
use context::{Command, flags_forbidden_for_cmd};
use rustc::version;
use exit_codes::{BAD_FLAG_CODE};
use rustc::driver::{session};

use usage;

use extra::{getopts};
use std::{result};
use std::hashmap::HashSet;

///  Result of parsing command line arguments
pub struct ParseResult {
    // Command
    command: Command,
    // Args
    args: ~[~str],
    // Parsed command line flags
    context: Context,
    // Path to system root
    sysroot: Option<~str>
}

/// Parses command line arguments of rustpkg.
/// Returns a triplet (command, remaining_args, context)
pub fn parse_args(args: &[~str]) -> Result<ParseResult, int> {
    let opts = ~[ getopts::optflag("h"), getopts::optflag("help"),
                                        getopts::optflag("no-link"),
                                        getopts::optflag("no-trans"),
                 // n.b. Ignores different --pretty options for now
                                        getopts::optflag("pretty"),
                                        getopts::optflag("parse-only"),
                 getopts::optflag("S"), getopts::optflag("assembly"),
                 getopts::optmulti("c"), getopts::optmulti("cfg"),
                 getopts::optflag("v"), getopts::optflag("version"),
                 getopts::optflag("r"), getopts::optflag("rust-path-hack"),
                                        getopts::optopt("sysroot"),
                                        getopts::optflag("emit-llvm"),
                                        getopts::optopt("linker"),
                                        getopts::optopt("link-args"),
                                        getopts::optopt("opt-level"),
                 getopts::optflag("O"),
                                        getopts::optflag("save-temps"),
                                        getopts::optopt("target"),
                                        getopts::optopt("target-cpu"),
                 getopts::optmulti("Z")                                   ];
    let matches = &match getopts::getopts(args, opts) {
        result::Ok(m) => m,
        result::Err(f) => {
            error!("{}", f.to_err_msg());
            return Err(1);
        }
    };
    let no_link = matches.opt_present("no-link");
    let no_trans = matches.opt_present("no-trans");
    let supplied_sysroot = matches.opt_str("sysroot");
    let generate_asm = matches.opt_present("S") ||
        matches.opt_present("assembly");
    let parse_only = matches.opt_present("parse-only");
    let pretty = matches.opt_present("pretty");
    let emit_llvm = matches.opt_present("emit-llvm");

    if matches.opt_present("h") ||
       matches.opt_present("help") {
         usage::general();
         return Err(0);
    }

    if matches.opt_present("v") ||
       matches.opt_present("version") {
        version(args[0]);
        return Err(0);
    }

    let use_rust_path_hack = matches.opt_present("r") ||
                             matches.opt_present("rust-path-hack");

    let linker = matches.opt_str("linker");
    let link_args = matches.opt_str("link-args");
    let cfgs = matches.opt_strs("cfg") + matches.opt_strs("c");
    let mut user_supplied_opt_level = true;
    let opt_level = match matches.opt_str("opt-level") {
        Some(~"0") => session::No,
        Some(~"1") => session::Less,
        Some(~"2") => session::Default,
        Some(~"3") => session::Aggressive,
        _ if matches.opt_present("O") => session::Default,
        _ => {
            user_supplied_opt_level = false;
            session::No
        }
    };

    let save_temps = matches.opt_present("save-temps");
    let target     = matches.opt_str("target");
    let target_cpu = matches.opt_str("target-cpu");
    let experimental_features = {
        let strs = matches.opt_strs("Z");
        if matches.opt_present("Z") {
            Some(strs)
        }
        else {
            None
        }
    };

    let mut args = matches.free.clone();
    args.shift();

    if (args.len() < 1) {
        usage::general();
        return Err(1);
    }

    let rustc_flags = RustcFlags {
        linker: linker,
        link_args: link_args,
        optimization_level: opt_level,
        compile_upto: if no_trans {
            Trans
        } else if no_link {
            Link
        } else if pretty {
            Pretty
        } else if parse_only {
            Analysis
        } else if emit_llvm && generate_asm {
            LLVMAssemble
        } else if generate_asm {
            Assemble
        } else if emit_llvm {
            LLVMCompileBitcode
        } else {
            Nothing
        },
        save_temps: save_temps,
        target: target,
        target_cpu: target_cpu,
        additional_library_paths:
            HashSet::new(), // No way to set this from the rustpkg command line
        experimental_features: experimental_features
    };

    let cmd_opt = args.iter().filter_map( |s| from_str(s.clone())).next();
    let command = match(cmd_opt){
        None => {
            debug!("No legal command. Returning 0");
            usage::general();
            return Err(0);
        }
        Some(cmd) => {
            let bad_option = flags_forbidden_for_cmd(&rustc_flags,
                                                              cfgs,
                                                              cmd,
                                                              user_supplied_opt_level);
            if bad_option {
                usage::usage_for_command(cmd);
                debug!("Bad  option, returning BAD_FLAG_CODE");
                return Err(BAD_FLAG_CODE);
            } else {
                cmd
            }
        }
    };

    // Pop off all flags, plus the command
    let mut remaining_args: ~[~str] = args.iter().skip_while(|&s| {
        let maybe_command: Option<Command> = from_str(*s);
        maybe_command.is_none()
    }).map(|s| s.clone()).collect();
    remaining_args.shift();

    let context = Context{
        rustc_flags: rustc_flags,
        cfgs: cfgs,
        use_rust_path_hack: use_rust_path_hack,
    };
    Ok(ParseResult {
        command:  command,
        args: remaining_args,
        context: context,
        sysroot: supplied_sysroot
    })
}

<|endoftext|>