<commit_before>#![feature(rust_2018_preview, nll)]

use std::cmp;
use std::collections as col;
use std::ops;
use std::rc;

#[derive(Clone, Debug)]
pub struct Layout(rc::Rc<LayoutContents>);

impl Layout {
    fn with_contents(contents: LayoutContents) -> Layout {
        Layout(rc::Rc::new(contents))
    }

    fn contents(&self) -> &LayoutContents {
        &*self.0
    }

    pub fn text(lit_text: String) -> Layout {
        Layout::with_contents(LayoutContents::Text(lit_text))
    }

    pub fn choice(first: &Layout, second: &Layout) -> Layout {
        // We should canonicalize this so that the choices are always in the same order.
        Layout::with_contents(LayoutContents::Choice(first.clone(), second.clone()))
    }

    pub fn stack(top: &Layout, bottom: &Layout) -> Layout {
        Layout::with_contents(LayoutContents::Stack(top.clone(), bottom.clone()))
    }

    pub fn juxtapose(left: &Layout, right: &Layout) -> Layout {
        let mut builder = JuxtaposeBuilder::new(right);
        builder.juxtapose(left)
    }
}

#[derive(Debug)]
enum LayoutContents {
    Text(String),
    Choice(Layout, Layout),
    Stack(Layout, Layout),
    Juxtapose(String, Layout),
}

struct JuxtaposeBuilder {
    right: Layout,
    memo: col::BTreeMap<*const LayoutContents, Layout>,
}

impl JuxtaposeBuilder {
    fn new(right: &Layout) -> Self {
        JuxtaposeBuilder { right: right.clone(), memo: col::BTreeMap::new() }
    }

    fn juxtapose(&mut self, left: &Layout) -> Layout {
        if let Some(v) = self.memo.get(&(left.contents() as *const LayoutContents)) {
            return v.clone();
        }

        let new_layout = match left.contents() {
            LayoutContents::Text(t) => Layout::with_contents(LayoutContents::Juxtapose(t.clone(), self.right.clone())),
            LayoutContents::Choice(first, second) =>
                Layout::with_contents(LayoutContents::Choice(self.juxtapose(first), self.juxtapose(second))),
            LayoutContents::Stack(top, bottom) =>
                Layout::with_contents(LayoutContents::Stack(top.clone(), self.juxtapose(bottom))),
            LayoutContents::Juxtapose(text, jux_right) =>
                Layout::with_contents(LayoutContents::Juxtapose(text.clone(), self.juxtapose(jux_right))),
        };

        self.memo.insert(left.contents() as *const LayoutContents, new_layout.clone());
        new_layout
    }
}

#[derive(Clone, Debug)]
enum ResolvedLayout {
    Text(String),
    Horiz(ResolvedLayoutRef, ResolvedLayoutRef),
    Vert(ResolvedLayoutRef, ResolvedLayoutRef),
}

#[derive(Clone, Debug)]
struct ResolvedLayoutRef(std::rc::Rc<ResolvedLayout>);

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
struct KnotColumn(u16);

#[derive(Clone, Debug)]
struct KnotData {
    // The layout as resolved.
    resolved_layout: ResolvedLayoutRef,

    /// The number of characters in the last line
    span: u16,

    /// The cost value at knot start
    intercept: f32,

    /// The cost increment per column after knot start
    rise: f32,
}

#[derive(Copy, Clone, Debug)]
enum SearchDirection {
    Less,
    LessEq,
    Greater,
    GreaterEq,
}

fn search_map<'a, T, K, V>(
    map: &'a col::BTreeMap<K, V>,
    search_key: &T,
    direction: SearchDirection,
) -> Option<(&'a K, &'a V)>
where
    K: std::borrow::Borrow<T> + cmp::Ord,
    T: cmp::Ord,
{
    match direction {
        SearchDirection::Less => {
            let mut range = map.range((ops::Bound::Unbounded, ops::Bound::Excluded(search_key)));
            range.next_back()
        }
        SearchDirection::LessEq => {
            let mut range = map.range((ops::Bound::Unbounded, ops::Bound::Included(search_key)));
            range.next_back()
        }
        SearchDirection::Greater => {
            let mut range = map.range((ops::Bound::Excluded(search_key), ops::Bound::Unbounded));
            range.next()
        }
        SearchDirection::GreaterEq => {
            let mut range = map.range((ops::Bound::Included(search_key), ops::Bound::Unbounded));
            range.next()
        }
    }
}

struct KnotSet {
    knots: col::BTreeMap<KnotColumn, KnotData>,
}

impl KnotSet {
    fn new_knot_set(
        knot_cols: &col::BTreeSet<KnotColumn>,
        func: impl Fn(KnotColumn) -> KnotData,
    ) -> KnotSet {
        let mut new_knots = col::BTreeMap::new();
        for &col in knot_cols {
            new_knots.insert(col, func(col));
        }
        KnotSet { knots: new_knots }
    }

    fn knot_data_at(&self, col: KnotColumn) -> KnotData {
        let (knot_col, knot_value) = search_map(&self.knots, &col, SearchDirection::LessEq)
            .expect("There should always be a knot at 0");
        let column_distance = col.0 - knot_col.0;
        KnotData {
            resolved_layout: knot_value.resolved_layout.clone(),
            span: knot_value.span,
            intercept: knot_value.intercept + knot_value.rise * (column_distance as f32),
            rise: knot_value.rise,
        }
    }

    fn knot_values(&self) -> col::BTreeSet<KnotColumn> {
        self.knots.keys().cloned().collect()
    }

    fn knot_values_between(&self, begin: KnotColumn, end: KnotColumn) -> col::BTreeSet<KnotColumn> {
        self.knots
            .range((ops::Bound::Included(begin), ops::Bound::Excluded(end)))
            .map(|(k, _)| *k)
            .collect()
    }

    fn left_knot_data(&self, col: KnotColumn) -> Option<(KnotColumn, &KnotData)> {
        search_map(&self.knots, &col, SearchDirection::Less).map(|(k, v)| (*k, v))
    }

    fn right_knot_data(&self, col: KnotColumn) -> Option<(KnotColumn, &KnotData)> {
        search_map(&self.knots, &col, SearchDirection::GreaterEq).map(|(k, v)| (*k, v))
    }

    fn intervals(&self) -> Vec<KnotInterval> {
        let mut knot_iter = self.knots.iter();
        let mut curr_col;
        let mut curr_data;
        let (first_col, first_data) = knot_iter.next().expect("KnotSets should have at least one element.");
        curr_col = *first_col;
        curr_data = first_data.clone();
        let mut result = Vec::with_capacity(self.knots.len());

        for (col, data) in knot_iter {
            result.push(KnotInterval {
                start: curr_col,
                end: Some(*col),
                data: curr_data,
            });
            curr_col = *col;
            curr_data = data.clone();
        }

        result.push(KnotInterval {
            start: curr_col,
            end: None,
            data: curr_data,
        });
        result
    }
}

struct KnotInterval {
    // The inclusive starting column of a knot. Minimum 0.
    start: KnotColumn,

    // The exclusive ending column of a knot, or None if unbounded.
    end: Option<KnotColumn>,

    data: KnotData,
}

struct KnotSetBuilder {
    margin: u16,
    overflow_cost: f32,
    newline_cost: f32,
}

impl KnotSetBuilder {
    pub fn new_text(&self, text: impl Into<String>) -> KnotSet {
        let text_str = text.into();
        let text_len = text_str.len() as u16;
        let layout = ResolvedLayoutRef(rc::Rc::new(ResolvedLayout::Text(text_str)));
        let mut knots = col::BTreeMap::new();
        if text_len < self.margin {
            let flat_data = KnotData {
                resolved_layout: layout.clone(),
                span: text_len,
                intercept: 0.0,
                rise: 0.0,
            };
            knots.insert(KnotColumn(0), flat_data);

            let rise_data = KnotData {
                resolved_layout: layout,
                span: text_len,
                intercept: 0.0,
                rise: self.overflow_cost,
            };

            knots.insert(KnotColumn(self.margin - text_len), rise_data);
        } else {
            let rise_data = KnotData {
                resolved_layout: layout,
                span: text_len,
                intercept: self.overflow_cost * ((text_len - self.margin) as f32),
                rise: self.overflow_cost,
            };
            knots.insert(KnotColumn(0), rise_data);
        }
        KnotSet { knots: knots }
    }

    pub fn new_vert(&self, top: KnotSet, bottom: KnotSet) -> KnotSet {
        let new_knot_values = top
            .knot_values()
            .union(&bottom.knot_values())
            .cloned()
            .collect();

        KnotSet::new_knot_set(&new_knot_values, |col| {
            let top_data = top.knot_data_at(col);
            let bottom_data = bottom.knot_data_at(col);
            KnotData {
                resolved_layout: ResolvedLayoutRef(rc::Rc::new(ResolvedLayout::Vert(
                    top_data.resolved_layout.clone(),
                    bottom_data.resolved_layout.clone(),
                ))),
                span: bottom_data.span,
                intercept: top_data.intercept + bottom_data.intercept + self.newline_cost,
                rise: top_data.rise + bottom_data.rise,
            }
        })
    }

    pub fn new_horiz(&self, _left: KnotSet, _right: KnotSet) -> KnotSet {
        unimplemented!()
    }

    pub fn new_choice(&self, _choice1: KnotSet, _choice2: KnotSet) -> KnotSet {
        unimplemented!()
    }
}
<commit_msg>Partial implementation of choice set<commit_after>#![feature(rust_2018_preview, nll)]

use std::cmp;
use std::collections as col;
use std::ops;
use std::rc;

#[derive(Clone, Debug)]
pub struct Layout(rc::Rc<LayoutContents>);

impl Layout {
    fn with_contents(contents: LayoutContents) -> Layout {
        Layout(rc::Rc::new(contents))
    }

    fn contents(&self) -> &LayoutContents {
        &*self.0
    }

    pub fn text(lit_text: String) -> Layout {
        Layout::with_contents(LayoutContents::Text(lit_text))
    }

    pub fn choice(first: &Layout, second: &Layout) -> Layout {
        // We should canonicalize this so that the choices are always in the same order.
        Layout::with_contents(LayoutContents::Choice(first.clone(), second.clone()))
    }

    pub fn stack(top: &Layout, bottom: &Layout) -> Layout {
        Layout::with_contents(LayoutContents::Stack(top.clone(), bottom.clone()))
    }

    pub fn juxtapose(left: &Layout, right: &Layout) -> Layout {
        let mut builder = JuxtaposeBuilder::new(right);
        builder.juxtapose(left)
    }
}

#[derive(Debug)]
enum LayoutContents {
    Text(String),
    Choice(Layout, Layout),
    Stack(Layout, Layout),
    Juxtapose(String, Layout),
}

struct JuxtaposeBuilder {
    right: Layout,
    memo: col::BTreeMap<*const LayoutContents, Layout>,
}

impl JuxtaposeBuilder {
    fn new(right: &Layout) -> Self {
        JuxtaposeBuilder {
            right: right.clone(),
            memo: col::BTreeMap::new(),
        }
    }

    fn juxtapose(&mut self, left: &Layout) -> Layout {
        if let Some(v) = self.memo.get(&(left.contents() as *const LayoutContents)) {
            return v.clone();
        }

        let new_layout = match left.contents() {
            LayoutContents::Text(t) => {
                Layout::with_contents(LayoutContents::Juxtapose(t.clone(), self.right.clone()))
            }
            LayoutContents::Choice(first, second) => Layout::with_contents(LayoutContents::Choice(
                self.juxtapose(first),
                self.juxtapose(second),
            )),
            LayoutContents::Stack(top, bottom) => {
                Layout::with_contents(LayoutContents::Stack(top.clone(), self.juxtapose(bottom)))
            }
            LayoutContents::Juxtapose(text, jux_right) => Layout::with_contents(
                LayoutContents::Juxtapose(text.clone(), self.juxtapose(jux_right)),
            ),
        };

        self.memo
            .insert(left.contents() as *const LayoutContents, new_layout.clone());
        new_layout
    }
}

#[derive(Clone, Debug)]
enum ResolvedLayout {
    Text(String),
    Horiz(ResolvedLayoutRef, ResolvedLayoutRef),
    Vert(ResolvedLayoutRef, ResolvedLayoutRef),
}

#[derive(Clone, Debug)]
struct ResolvedLayoutRef(std::rc::Rc<ResolvedLayout>);

impl ResolvedLayoutRef {
    fn new(inner: ResolvedLayout) -> ResolvedLayoutRef {
        ResolvedLayoutRef(rc::Rc::new(inner))
    }

    pub fn new_text(text: impl Into<String>) -> ResolvedLayoutRef {
        ResolvedLayoutRef::new(ResolvedLayout::Text(text.into()))
    }

    pub fn new_horiz(left: ResolvedLayoutRef, right: ResolvedLayoutRef) -> ResolvedLayoutRef {
        ResolvedLayoutRef::new(ResolvedLayout::Horiz(left, right))
    }

    pub fn new_vert(top: ResolvedLayoutRef, bottom: ResolvedLayoutRef) -> ResolvedLayoutRef {
        ResolvedLayoutRef::new(ResolvedLayout::Vert(top, bottom))
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
struct KnotColumn(u16);

#[derive(Clone, Debug)]
struct KnotData {
    // The layout as resolved.
    resolved_layout: ResolvedLayoutRef,

    /// The number of characters in the last line
    span: u16,

    /// The cost value at knot start
    intercept: f32,

    /// The cost increment per column after knot start
    rise: f32,
}

#[derive(Copy, Clone, Debug)]
enum SearchDirection {
    Less,
    LessEq,
    Greater,
    GreaterEq,
}

fn search_map<'a, T, K, V>(
    map: &'a col::BTreeMap<K, V>,
    search_key: &T,
    direction: SearchDirection,
) -> Option<(&'a K, &'a V)>
where
    K: std::borrow::Borrow<T> + cmp::Ord,
    T: cmp::Ord,
{
    match direction {
        SearchDirection::Less => {
            let mut range = map.range((ops::Bound::Unbounded, ops::Bound::Excluded(search_key)));
            range.next_back()
        }
        SearchDirection::LessEq => {
            let mut range = map.range((ops::Bound::Unbounded, ops::Bound::Included(search_key)));
            range.next_back()
        }
        SearchDirection::Greater => {
            let mut range = map.range((ops::Bound::Excluded(search_key), ops::Bound::Unbounded));
            range.next()
        }
        SearchDirection::GreaterEq => {
            let mut range = map.range((ops::Bound::Included(search_key), ops::Bound::Unbounded));
            range.next()
        }
    }
}

struct KnotSet {
    knots: col::BTreeMap<KnotColumn, KnotData>,
}

impl KnotSet {
    fn new_knot_set(
        knot_cols: &col::BTreeSet<KnotColumn>,
        func: impl Fn(KnotColumn) -> KnotData,
    ) -> KnotSet {
        let mut new_knots = col::BTreeMap::new();
        for &col in knot_cols {
            new_knots.insert(col, func(col));
        }
        KnotSet { knots: new_knots }
    }

    fn knot_data_at(&self, col: KnotColumn) -> KnotData {
        let (knot_col, knot_value) = search_map(&self.knots, &col, SearchDirection::LessEq)
            .expect("There should always be a knot at 0");
        let column_distance = col.0 - knot_col.0;
        KnotData {
            resolved_layout: knot_value.resolved_layout.clone(),
            span: knot_value.span,
            intercept: knot_value.intercept + knot_value.rise * (column_distance as f32),
            rise: knot_value.rise,
        }
    }

    fn knot_values(&self) -> col::BTreeSet<KnotColumn> {
        self.knots.keys().cloned().collect()
    }

    fn knot_values_between(
        &self,
        begin: KnotColumn,
        end: Option<KnotColumn>,
    ) -> col::BTreeSet<KnotColumn> {
        let right_bound = match end {
            Some(c) => ops::Bound::Excluded(c),
            None => ops::Bound::Unbounded,
        };
        self.knots
            .range((ops::Bound::Included(begin), right_bound))
            .map(|(k, _)| *k)
            .collect()
    }

    fn left_knot_data(&self, col: KnotColumn) -> Option<(KnotColumn, &KnotData)> {
        search_map(&self.knots, &col, SearchDirection::Less).map(|(k, v)| (*k, v))
    }

    fn right_knot_data(&self, col: KnotColumn) -> Option<(KnotColumn, &KnotData)> {
        search_map(&self.knots, &col, SearchDirection::GreaterEq).map(|(k, v)| (*k, v))
    }

    fn intervals(&self) -> Vec<KnotInterval> {
        let mut knot_iter = self.knots.iter();
        let mut curr_col;
        let mut curr_data;
        let (first_col, first_data) = knot_iter
            .next()
            .expect("KnotSets should have at least one element.");
        curr_col = *first_col;
        curr_data = first_data.clone();
        let mut result = Vec::with_capacity(self.knots.len());

        for (col, data) in knot_iter {
            result.push(KnotInterval {
                start: curr_col,
                end: Some(*col),
                data: curr_data,
            });
            curr_col = *col;
            curr_data = data.clone();
        }

        result.push(KnotInterval {
            start: curr_col,
            end: None,
            data: curr_data,
        });
        result
    }
}

struct KnotInterval {
    // The inclusive starting column of a knot. Minimum 0.
    start: KnotColumn,

    // The exclusive ending column of a knot, or None if unbounded.
    end: Option<KnotColumn>,

    data: KnotData,
}

fn column_intervals(columns: &col::BTreeSet<KnotColumn>) -> Vec<(KnotColumn, Option<KnotColumn>)> {
    let mut iter = columns.iter();
    let first = iter.next().expect("Always at least a knot at 0");
    let mut curr_start = *first;
    let mut result = Vec::new();
    for &col in iter {
        result.push((curr_start, Some(col)));
        curr_start = col;
    }
    result.push((curr_start, None));
    result
}

struct KnotSetBuilder {
    margin: u16,
    overflow_cost: f32,
    newline_cost: f32,
}

impl KnotSetBuilder {
    pub fn new_text(&self, text: impl Into<String>) -> KnotSet {
        let text_str = text.into();
        let text_len = text_str.len() as u16;
        let layout = ResolvedLayoutRef::new_text(text_str);
        let mut knots = col::BTreeMap::new();
        if text_len < self.margin {
            let flat_data = KnotData {
                resolved_layout: layout.clone(),
                span: text_len,
                intercept: 0.0,
                rise: 0.0,
            };
            knots.insert(KnotColumn(0), flat_data);

            let rise_data = KnotData {
                resolved_layout: layout,
                span: text_len,
                intercept: 0.0,
                rise: self.overflow_cost,
            };

            knots.insert(KnotColumn(self.margin - text_len), rise_data);
        } else {
            let rise_data = KnotData {
                resolved_layout: layout,
                span: text_len,
                intercept: self.overflow_cost * ((text_len - self.margin) as f32),
                rise: self.overflow_cost,
            };
            knots.insert(KnotColumn(0), rise_data);
        }
        KnotSet { knots: knots }
    }

    pub fn new_vert(&self, top: KnotSet, bottom: KnotSet) -> KnotSet {
        let new_knot_values = top
            .knot_values()
            .union(&bottom.knot_values())
            .cloned()
            .collect();

        KnotSet::new_knot_set(&new_knot_values, |col| {
            let top_data = top.knot_data_at(col);
            let bottom_data = bottom.knot_data_at(col);
            KnotData {
                resolved_layout: ResolvedLayoutRef::new_vert(
                    top_data.resolved_layout.clone(),
                    bottom_data.resolved_layout.clone(),
                ),
                span: bottom_data.span,
                intercept: top_data.intercept + bottom_data.intercept + self.newline_cost,
                rise: top_data.rise + bottom_data.rise,
            }
        })
    }

    pub fn new_horiz(&self, left: impl Into<String>, right: KnotSet) -> KnotSet {
        let left = left.into();
        let left_width = left.len() as u16;

        let shifted_left_knot_values = right
            .knot_values_between(KnotColumn(left_width), None)
            .into_iter()
            .map(|KnotColumn(col)| KnotColumn(col - left_width))
            .collect();

        let text_knot_set = self.new_text(left);
        let new_knot_values: col::BTreeSet<_> = text_knot_set
            .knot_values()
            .union(&shifted_left_knot_values)
            .cloned()
            .collect();

        KnotSet::new_knot_set(&new_knot_values, |col| {
            let right_knot_col = KnotColumn(col.0 + left_width);
            let left_data = text_knot_set.knot_data_at(col);
            let right_data = right.knot_data_at(right_knot_col);

            let sub_intercept_factor;
            let sub_rise_factor;
            if col.0 > self.margin {
                sub_intercept_factor = (col.0 - self.margin) as f32 * self.overflow_cost;
                sub_rise_factor = self.overflow_cost;
            } else {
                sub_intercept_factor = 0.0;
                sub_rise_factor = 0.0;
            }
            KnotData {
                resolved_layout: ResolvedLayoutRef::new_horiz(
                    right_data.resolved_layout.clone(),
                    left_data.resolved_layout.clone(),
                ),
                span: left_data.span + right_data.span,
                intercept: left_data.intercept + right_data.intercept - sub_intercept_factor,
                rise: left_data.rise + right_data.rise - sub_rise_factor,
            }
        })
    }

    pub fn new_choice(&self, choice1: KnotSet, choice2: KnotSet) -> KnotSet {
        // Set "L" in the paper
        let base_knots: col::BTreeSet<_> = choice1
            .knot_values()
            .union(&choice2.knot_values())
            .cloned()
            .collect();
        let mut extra_knots = col::BTreeSet::new();
        for (start, end_opt) in column_intervals(&base_knots) {
            // Find the Chi_K value for this range.
            let choice1_data = choice1.knot_data_at(start);
            let choice2_data = choice2.knot_data_at(start);

            let chi_k = (choice2_data.intercept - choice1_data.intercept)
                / (choice1_data.rise - choice2_data.rise);

            let intersect_delta = chi_k.ceil() as u16;

            let in_range = match end_opt {
                Some(k) => start.0 + intersect_delta < k.0,
                None => false,
            };

            if in_range {
                extra_knots.insert(KnotColumn(start.0 + intersect_delta));
            }
        }

        let all_knots: col::BTreeSet<_> = base_knots.union(&extra_knots).cloned().collect();

        unimplemented!()
    }
}
<|endoftext|>


<commit_before>
extern crate rustc_serialize;
extern crate amqp;

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

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

    let queue_name = "fbp";
    let consumer_name = channel.basic_consume(participant_discovered as ConsumerCallBackFn, queue_name,
                                                "", false, false, false, false, Table::new());
    println!("listening for discovery messages");
}

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

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

type SendFunction = fn(String, Vec<u8>);
pub type ProcessFunction = fn(Vec<u8>) -> Result<Vec<u8>, Vec<u8>>;
pub struct Participant {
    pub info: ParticipantInfo,
    pub process: ProcessFunction,
}

struct Connection {
    session: Session,
    channel: Channel,
}

// FIXME: wrap in protocol/command/payload envelope
fn create_queue_and_send(channel: &mut Channel, queue_name: &str, payload: String) {

    let queue_declare = channel.queue_declare(queue_name, false, true, false, false, false, Table::new());
    queue_declare.expect("queue creation failed");
    let content_type = Some("application/json".to_string());
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };
    println!("sending on {}: {}", queue_name, payload);
    let res = channel.basic_publish("", queue_name, true, false, props, payload.into_bytes());
    res.expect("send on new queue failed");
}

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

    let payload = json::encode(&info).unwrap();
    create_queue_and_send(channel, queue_name, payload);
}

struct PortConsumer {
    process: ProcessFunction,
    portname: String,
    outqueue: String, // FIXME: allow sending on any port, also multiple times
}

fn send_out(channel: &mut Channel, exchange: String, data: Vec<u8>) {

    let routing_key = "".to_string();
    let content_type = Some("application/json".to_string()); // TODO: should be parameter
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };
    let s = channel.basic_publish(exchange, routing_key, true, false, props, data);
    s.expect("failed to send");
    println!("sent output");
}

impl Consumer for PortConsumer {
    fn handle_delivery(&mut self,
                       channel: &mut Channel,
                       deliver: protocol::basic::Deliver,
                       headers: protocol::basic::BasicProperties,
                       body: Vec<u8>) {

        println!("calling process()");
        let f = self.process;
        let res = f(body);
        println!("process() returned");

        if res.is_ok() {
            println!("ACKing and sending");
            send_out(channel, self.outqueue.to_string(), res.unwrap());
            let r = channel.basic_ack(deliver.delivery_tag, false);
        } else {
            let r = channel.basic_nack(deliver.delivery_tag, true, true);
        }
        // FIXME: send error data
    }
}

// FIXME: actually call ProcessFunction
fn setup_inport(participant: &Participant, port: &ParticipantPort, connection: &mut Connection) {
    println!("setup inport: {}", port.queue.to_string());

    let consumer = PortConsumer {
        process: participant.process,
        portname: port.id.to_string(),
        outqueue: participant.info.outports[0].queue.to_string(),
    };

    // create
    let declare = connection.channel.queue_declare(port.queue.to_string(), false, true, false, false, false, Table::new());
    declare.expect("inport queue creation failed");

    // subscribe
    let q = port.queue.to_string();
    let cons = connection.channel.basic_consume(consumer, q,
                                                "".to_string(), false, false, false, false, Table::new());

    cons.expect("iport setup failed");

    println!("setup inport done: {:?}, {:?}", port.id.to_string(), port.queue.to_string());
}

fn setup_outport(participant: &Participant, port: &ParticipantPort, connection: &mut Connection) {

    let exchange_type = "fanout".to_string();
    let declare = connection.channel.exchange_declare(port.queue.to_string(), exchange_type,
                                                    false, true, false, false, false, Table::new());
    declare.expect("outport setup failed");

    println!("setup outport done: {:?}, {:?}", port.id.to_string(), port.queue.to_string());
}

fn start_participant(participant: &Participant) -> Connection {
    let mut session = Session::new(Options{vhost: "/", .. Default::default()}).ok().expect("Can't create session");
    let mut channel = session.open_channel(1).expect("channel");

    send_discovery(&mut channel, &participant.info);

    let mut conn = Connection { session: session, channel: channel };

    setup_inport(&participant, &participant.info.inports[0], &mut conn);
    setup_outport(&participant, &participant.info.outports[0], &mut conn);

    return conn;
}

// for debugging
fn stop_participant(participant: &Participant, connection: &mut Connection) {

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

// XXX: seems rust-amqp makes program hangs forever if error occurs / channel is borked?
// TODO: pass port info in/out of process()
// TODO: respect MSGFLO_BROKER envvar
// TODO: setup the msgflo hetro automated test
// TODO: move into library, have entrypoint(s) usable from outside
// TODO: nicer way to declare ports? ideally they are enums not stringly typed?
pub fn participant_main(p: Participant) {
    let mut c = start_participant(&p);
    c.channel.start_consuming();
    stop_participant(&p, &mut c);
}
<commit_msg>Setup ports before sending discovery message<commit_after>
extern crate rustc_serialize;
extern crate amqp;

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

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

    let queue_name = "fbp";
    let consumer_name = channel.basic_consume(participant_discovered as ConsumerCallBackFn, queue_name,
                                                "", false, false, false, false, Table::new());
    println!("listening for discovery messages");
}

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

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

type SendFunction = fn(String, Vec<u8>);
pub type ProcessFunction = fn(Vec<u8>) -> Result<Vec<u8>, Vec<u8>>;
pub struct Participant {
    pub info: ParticipantInfo,
    pub process: ProcessFunction,
}

struct Connection {
    session: Session,
    channel: Channel,
}

// FIXME: wrap in protocol/command/payload envelope
fn create_queue_and_send(channel: &mut Channel, queue_name: &str, payload: String) {

    let queue_declare = channel.queue_declare(queue_name, false, true, false, false, false, Table::new());
    queue_declare.expect("queue creation failed");
    let content_type = Some("application/json".to_string());
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };
    println!("sending on {}: {}", queue_name, payload);
    let res = channel.basic_publish("", queue_name, true, false, props, payload.into_bytes());
    res.expect("send on new queue failed");
}

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

    let payload = json::encode(&info).unwrap();
    create_queue_and_send(channel, queue_name, payload);
}

struct PortConsumer {
    process: ProcessFunction,
    portname: String,
    outqueue: String, // FIXME: allow sending on any port, also multiple times
}

fn send_out(channel: &mut Channel, exchange: String, data: Vec<u8>) {

    let routing_key = "".to_string();
    let content_type = Some("application/json".to_string()); // TODO: should be parameter
    let props = protocol::basic::BasicProperties { content_type: content_type, ..Default::default() };
    let s = channel.basic_publish(exchange, routing_key, true, false, props, data);
    s.expect("failed to send");
    println!("sent output");
}

impl Consumer for PortConsumer {
    fn handle_delivery(&mut self,
                       channel: &mut Channel,
                       deliver: protocol::basic::Deliver,
                       headers: protocol::basic::BasicProperties,
                       body: Vec<u8>) {

        println!("calling process()");
        let f = self.process;
        let res = f(body);
        println!("process() returned");

        if res.is_ok() {
            println!("ACKing and sending");
            send_out(channel, self.outqueue.to_string(), res.unwrap());
            let r = channel.basic_ack(deliver.delivery_tag, false);
        } else {
            let r = channel.basic_nack(deliver.delivery_tag, true, true);
        }
        // FIXME: send error data
    }
}

// FIXME: actually call ProcessFunction
fn setup_inport(participant: &Participant, port: &ParticipantPort, connection: &mut Connection) {
    println!("setup inport: {}", port.queue.to_string());

    let consumer = PortConsumer {
        process: participant.process,
        portname: port.id.to_string(),
        outqueue: participant.info.outports[0].queue.to_string(),
    };

    // create
    let declare = connection.channel.queue_declare(port.queue.to_string(), false, true, false, false, false, Table::new());
    declare.expect("inport queue creation failed");

    // subscribe
    let q = port.queue.to_string();
    let cons = connection.channel.basic_consume(consumer, q,
                                                "".to_string(), false, false, false, false, Table::new());

    cons.expect("iport setup failed");

    println!("setup inport done: {:?}, {:?}", port.id.to_string(), port.queue.to_string());
}

fn setup_outport(participant: &Participant, port: &ParticipantPort, connection: &mut Connection) {

    let exchange_type = "fanout".to_string();
    let declare = connection.channel.exchange_declare(port.queue.to_string(), exchange_type,
                                                    false, true, false, false, false, Table::new());
    declare.expect("outport setup failed");

    println!("setup outport done: {:?}, {:?}", port.id.to_string(), port.queue.to_string());
}

fn start_participant(participant: &Participant) -> Connection {
    let mut session = Session::new(Options{vhost: "/", .. Default::default()}).ok().expect("Can't create session");
    let mut channel = session.open_channel(1).expect("channel");

    let mut conn = Connection { session: session, channel: channel };

    // setup ports
    setup_inport(&participant, &participant.info.inports[0], &mut conn);
    setup_outport(&participant, &participant.info.outports[0], &mut conn);

    // send MsgFlo participant discovery message
    send_discovery(&mut conn.channel, &participant.info);

    return conn;
}

// for debugging
fn stop_participant(participant: &Participant, connection: &mut Connection) {

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

// XXX: seems rust-amqp makes program hangs forever if error occurs / channel is borked?
// TODO: pass port info in/out of process()
// TODO: respect MSGFLO_BROKER envvar
// TODO: setup the msgflo hetro automated test
// TODO: move into library, have entrypoint(s) usable from outside
// TODO: nicer way to declare ports? ideally they are enums not stringly typed?
pub fn participant_main(p: Participant) {
    let mut c = start_participant(&p);
    c.channel.start_consuming();
    stop_participant(&p, &mut c);
}
<|endoftext|>


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

pub trait Capacity {

  fn bytes(&self) -> i64;

  fn kilobytes(&self) -> i64 {
    self.bytes().rotate_left(10)
  }

  fn megabytes(&self) -> i64 {
    self.kilobytes().rotate_left(10)
  }

  fn gigabytes(&self) -> i64 {
    self.megabytes().rotate_left(10)
  }

  fn terabytes(&self) -> i64 {
    self.gigabytes().rotate_left(10)
  }

  fn petabytes(&self) -> i64 {
    self.terabytes().rotate_left(10)
  }

  fn exabytes(&self) -> i64 {
    self.petabytes().rotate_left(10)
  }
}

impl Capacity for i64 {
  fn bytes(&self) -> i64 {
    *self
  }
}

impl Capacity for Size {
  fn bytes(&self) -> i64 {
    (*self).bytes
  }
}

pub struct Size {
  bytes: i64
}

impl Into<Size> for i64 {
  fn into(self) -> Size {
    Size { bytes: self }
  }
}

#[test]
fn test_bytes() {
  assert_eq!(1.bytes(), 1);
}

#[test]
fn test_kilobytes() {
  assert_eq!(1.kilobytes(), 1024)
}

#[test]
fn test_megabytes() {
  assert_eq!(1.megabytes(), 1048576)
}

#[test]
fn test_gigabytes() {
  assert_eq!(1.gigabytes(), 1073741824)
}

#[test]
fn test_terabytes() {
  assert_eq!(1.terabytes(), 1099511627776)
}

#[test]
fn test_petabytes() {
  assert_eq!(1.petabytes(), 1125899906842624)
}

#[test]
fn test_exabytes() {
  assert_eq!(1.exabytes(), 1152921504606846976)
}
<commit_msg>introduce capacity<commit_after>use std::convert::Into;
use std::str::FromStr;

const BYTE: i64 = 1;
const KILOBYTE: i64 = BYTE << 10;
const MEGABYTE: i64 = KILOBYTE << 10;
const GIGABYTE: i64 = MEGABYTE << 10;
const TERABYTE: i64 = GIGABYTE << 10;
const PETABYTE: i64 = TERABYTE << 10;
const EXABYTE: i64 = PETABYTE << 10;

pub trait Capacity {

  fn bytes(&self) -> i64;

  fn kilobytes(&self) -> i64 {
    self.bytes().rotate_left(10)
  }

  fn megabytes(&self) -> i64 {
    self.kilobytes().rotate_left(10)
  }

  fn gigabytes(&self) -> i64 {
    self.megabytes().rotate_left(10)
  }

  fn terabytes(&self) -> i64 {
    self.gigabytes().rotate_left(10)
  }

  fn petabytes(&self) -> i64 {
    self.terabytes().rotate_left(10)
  }

  fn exabytes(&self) -> i64 {
    self.petabytes().rotate_left(10)
  }

  fn capacity(&self) -> String {
    match self.bytes() {
      small if small < KILOBYTE =>
        stringify!(small).to_owned(),
      large => {
        let units = vec![
          ('E', EXABYTE),
          ('P', PETABYTE),
          ('T', TERABYTE),
          ('G', GIGABYTE),
          ('M', MEGABYTE),
          ('K', KILOBYTE)
        ];
        for (suffix, size) in units {
          if large == size {
            return format!("1{}", suffix)
          } else if large > size {
            let sized = (large as f64) / (size as f64);
            let round = (sized * 100.0).round() / 100.0;
            return format!("{:.1}{}", round, suffix)
          }
        }
        unreachable!()
      }
    }
  }
}

impl Capacity for i64 {
  fn bytes(&self) -> i64 {
    *self
  }
}

impl Capacity for Bytes {
  fn bytes(&self) -> i64 {
    (*self).size
  }
}

pub struct Bytes {
  size: i64
}

impl Into<Bytes> for i64 {
  fn into(self) -> Bytes {
    Bytes { size: self }
  }
}

impl FromStr for Bytes {
  type Err = String;
  fn from_str(s: &str) -> Result<Bytes, String> {
    Err(s.to_owned())
  }
}

#[test]
fn test_bytes() {
  assert_eq!(1.bytes(), 1);
}

#[test]
fn test_kilobytes() {
  assert_eq!(1.kilobytes(), 1024)
}

#[test]
fn test_megabytes() {
  assert_eq!(1.megabytes(), 1048576)
}

#[test]
fn test_gigabytes() {
  assert_eq!(1.gigabytes(), 1073741824)
}

#[test]
fn test_terabytes() {
  assert_eq!(1.terabytes(), 1099511627776)
}

#[test]
fn test_petabytes() {
  assert_eq!(1.petabytes(), 1125899906842624)
}

#[test]
fn test_exabytes() {
  assert_eq!(1.exabytes(), 1152921504606846976)
}

#[test]
fn test_kilobytes_capactity() {
  let half = 1.kilobytes() / 2;
  assert_eq!((1.kilobytes() + half).capacity(), "1.5K".to_owned())
}

#[test]
fn test_megabytes_capactity() {
  let half = 1.megabytes() / 2;
  assert_eq!((1.megabytes() + half).capacity(), "1.5M".to_owned())
}

#[test]
fn test_gigabytes_capactity() {
  let half = 1.gigabytes() / 2;
  assert_eq!((1.gigabytes() + half).capacity(), "1.5G".to_owned())
}

#[test]
fn test_terabytes_capactity() {
  let half = 1.terabytes() / 2;
  assert_eq!((1.terabytes() + half).capacity(), "1.5T".to_owned())
}

#[test]
fn test_petabytes_capactity() {
  let half = 1.petabytes() / 2;
  assert_eq!((1.petabytes() + half).capacity(), "1.5P".to_owned())
}

#[test]
fn test_exabytes_capactity() {
  let half = 1.exabytes() / 2;
  assert_eq!((1.exabytes() + half).capacity(), "1.5E".to_owned())
}
<|endoftext|>


<commit_before>//! # Convert Sort Expression to SQL
//!
//! Converts 'sort format' to 'SQL format'. E.g.:
//!
//! - `-id` -> `id DESC NULLS LAST`
//! - `id`  -> `id ASC NULLS LAST`
//! - `id-` -> `id ASC NULLS FIRST`
//! - `-aired,id` -> `aired DESC NULLS LAST, id ASC NULLS LAST`
//!
//! # Example
//!
//! ```
//! # use sort_str_to_sql::{sort_str_to_sql};
//! assert!(
//!   sort_str_to_sql("-id") ==
//!   Some("id DESC NULLS LAST".to_string())
//! )
//! assert!(
//!   sort_str_to_sql("-id,+aired-") ==
//!   Some("id DESC NULLS LAST, aired ASC NULLS FIRST".to_string())
//! )
//! ```
//!
//! (See tests for more examples.)
#![crate_name = "sort_str_to_sql"]

#![feature(phase)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
#[cfg(test)] extern crate test;

static SORT_STR_FORMAT: regex::Regex = regex!(
    r"^(?P<order>[+-]?)(?P<field>[\w.]+)(?P<nulls>[-]?)$"
);

/// Convert One Sort Expression to SQL
fn convert_one_sort_str_field_to_sql(sort_str: &str) -> Option<String> {
    let fields = match SORT_STR_FORMAT.captures(sort_str) {
        Some(captures) => captures,
        None => return None,
    };

    let field = fields.name("field");
    let order = match fields.name("order") {
        "-" => "DESC",
        _   => "ASC",
    };
    let nulls = match fields.name("nulls") {
        "-" => "NULLS FIRST",
        _   => "NULLS LAST",
    };

    let sql = format!("{} {} {}", field, order, nulls);
    return Some(sql);
}

/// Convert Sort Expression to SQL
pub fn sort_str_to_sql(sort_str: &str) -> Option<String> {
    let split_mark = regex!("[,]");

    let sql_array: Vec<String> = split_mark.split(sort_str)
    .map(convert_one_sort_str_field_to_sql)
    .filter(|input| input.is_some())
    .map(|input| input.unwrap())
    .collect();

    let sql = sql_array.connect(", ");
    return if sql.as_slice() == "" { None } else { Some(sql) };
}

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

    use super::{sort_str_to_sql};

    #[test]
    fn it_works() {
        let tests = vec![
            // Correct inputs
            ("id", Some("id ASC NULLS LAST")),
            ("+id", Some("id ASC NULLS LAST")),
            ("-id", Some("id DESC NULLS LAST")),
            ("id-", Some("id ASC NULLS FIRST")),
            ("+id-", Some("id ASC NULLS FIRST")),
            ("-id-", Some("id DESC NULLS FIRST")),
            ("show.id", Some("show.id ASC NULLS LAST")),
            ("-id,aired-", Some("id DESC NULLS LAST, aired ASC NULLS FIRST")),
            ("-id,+aired-", Some("id DESC NULLS LAST, aired ASC NULLS FIRST")),
            ("+id-,show.id", Some("id ASC NULLS FIRST, show.id ASC NULLS LAST")),

            // Incorrect inputs
            ("lol what", None),
            ("+-id-", None),

            // Partially correct inputs
            ("id,++aired+", Some("id ASC NULLS LAST")),
            ("?id,+aired-", Some("aired ASC NULLS FIRST")),
        ];

        for &(input, _sql) in tests.iter() {
            let sql = match _sql { None => None, Some(s) => Some(s.to_string())};
            assert!(
                sort_str_to_sql(input) == sql,
                "FAILED `{}` => `{}` NOT `{}`", input, sort_str_to_sql(input), sql
            )
        }
    }

    #[bench]
    fn bench_simple(b: &mut Bencher) {
        b.iter(|| sort_str_to_sql("id"));
    }

    #[bench]
    fn bench_complex(b: &mut Bencher) {
        b.iter(|| sort_str_to_sql("-date,+id-,show.id"));
    }

}
<commit_msg>Split String using `.split` Instead of Regex<commit_after>//! # Convert Sort Expression to SQL
//!
//! Converts 'sort format' to 'SQL format'. E.g.:
//!
//! - `-id` -> `id DESC NULLS LAST`
//! - `id`  -> `id ASC NULLS LAST`
//! - `id-` -> `id ASC NULLS FIRST`
//! - `-aired,id` -> `aired DESC NULLS LAST, id ASC NULLS LAST`
//!
//! # Example
//!
//! ```
//! # use sort_str_to_sql::{sort_str_to_sql};
//! assert!(
//!   sort_str_to_sql("-id") ==
//!   Some("id DESC NULLS LAST".to_string())
//! )
//! assert!(
//!   sort_str_to_sql("-id,+aired-") ==
//!   Some("id DESC NULLS LAST, aired ASC NULLS FIRST".to_string())
//! )
//! ```
//!
//! (See tests for more examples.)
#![crate_name = "sort_str_to_sql"]

#![feature(phase)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
#[cfg(test)] extern crate test;

static SORT_STR_FORMAT: regex::Regex = regex!(
    r"^(?P<order>[+-]?)(?P<field>[\w.]+)(?P<nulls>[-]?)$"
);

/// Convert One Sort Expression to SQL
fn convert_one_sort_str_field_to_sql(sort_str: &str) -> Option<String> {
    let fields = match SORT_STR_FORMAT.captures(sort_str) {
        Some(captures) => captures,
        None => return None,
    };

    let field = fields.name("field");
    let order = match fields.name("order") {
        "-" => "DESC",
        _   => "ASC",
    };
    let nulls = match fields.name("nulls") {
        "-" => "NULLS FIRST",
        _   => "NULLS LAST",
    };

    let sql = format!("{} {} {}", field, order, nulls);
    return Some(sql);
}

/// Convert Sort Expression to SQL
pub fn sort_str_to_sql(sort_str: &str) -> Option<String> {
    let sql_array: Vec<String> = sort_str.split(',')
    .map(convert_one_sort_str_field_to_sql)
    .filter(|input| input.is_some())
    .map(|input| input.unwrap())
    .collect();

    let sql = sql_array.connect(", ");
    return if sql.as_slice() == "" { None } else { Some(sql) };
}

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

    use super::{sort_str_to_sql};

    #[test]
    fn it_works() {
        let tests = vec![
            // Correct inputs
            ("id", Some("id ASC NULLS LAST")),
            ("+id", Some("id ASC NULLS LAST")),
            ("-id", Some("id DESC NULLS LAST")),
            ("id-", Some("id ASC NULLS FIRST")),
            ("+id-", Some("id ASC NULLS FIRST")),
            ("-id-", Some("id DESC NULLS FIRST")),
            ("show.id", Some("show.id ASC NULLS LAST")),
            ("-id,aired-", Some("id DESC NULLS LAST, aired ASC NULLS FIRST")),
            ("-id,+aired-", Some("id DESC NULLS LAST, aired ASC NULLS FIRST")),
            ("+id-,show.id", Some("id ASC NULLS FIRST, show.id ASC NULLS LAST")),

            // Incorrect inputs
            ("lol what", None),
            ("+-id-", None),

            // Partially correct inputs
            ("id,++aired+", Some("id ASC NULLS LAST")),
            ("?id,+aired-", Some("aired ASC NULLS FIRST")),
        ];

        for &(input, _sql) in tests.iter() {
            let sql = match _sql { None => None, Some(s) => Some(s.to_string())};
            assert!(
                sort_str_to_sql(input) == sql,
                "FAILED `{}` => `{}` NOT `{}`", input, sort_str_to_sql(input), sql
            )
        }
    }

    #[bench]
    fn bench_simple(b: &mut Bencher) {
        b.iter(|| sort_str_to_sql("id"));
    }

    #[bench]
    fn bench_complex(b: &mut Bencher) {
        b.iter(|| sort_str_to_sql("-date,+id-,show.id"));
    }

}
<|endoftext|>


<commit_before>#![feature(test)]
#[cfg(test)]
extern crate test;
#[cfg(test)]
extern crate rand;
#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
extern crate regex;

pub mod afsort {

    pub fn sort<'a, T>(vec: &'a mut [T])
    where
        T: AsRef<[u8]>,
    {
        sort_by(vec, (|s| s.as_ref()));
    }

    pub fn sort_by<T, F>(vec: &mut [T], to_slice: F)
    where
        F: Fn(&T) -> &[u8],
    {
        sort_req(vec, &to_slice, 0);
    }

    fn sort_req<T, F>(vec: &mut [T], to_slice: &F, depth: usize)
    where
        F: Fn(&T) -> &[u8],
    {
        if vec.len() <= 32 {
            vec.sort_unstable_by(|e1, e2| to_slice(e1).cmp(to_slice(e2)));
            return;
        }
        let mut min = u16::max_value();
        let mut max = 0u16;
        for elem in vec.iter() {
            let val = to_slice(elem);
            if val.len() > depth {
                let radix_val = val[depth] as u16;
                if radix_val < min {
                    min = radix_val;
                }
                if radix_val > max {
                    max = radix_val;
                }
            }
        }
        if min == u16::max_value() {
            return;
        }

        let num_items = (max - min + 2) as usize;
        let mut counts: Vec<usize> = vec![0usize; num_items as usize];

        for elem in vec.iter() {
            let radix_val = radix_for_str(to_slice(elem), depth, min);
            counts[radix_val as usize] += 1;
        }

        let mut sum = 0usize;
        let mut offsets: Vec<usize> = vec![0usize; num_items as usize];
        for i in 0..num_items {
            offsets[i as usize] = sum;
            sum += counts[i as usize];
        }
        let mut next_free = offsets.clone();

        let mut block = 0usize;
        let mut i = 0usize;
        while block < num_items - 1 {
            if i >= offsets[block as usize + 1] as usize {
                block += 1;
            } else {
                let radix_val = radix_for_str(to_slice(&vec[i]), depth, min);
                if radix_val == block as u16 {
                    i += 1;
                } else {
                    vec.swap(i as usize, next_free[radix_val as usize] as usize);
                    next_free[radix_val as usize] += 1;
                }
            }
        }
        for i in 0..num_items - 1 {
            sort_req(
                &mut vec[offsets[i as usize] as usize..offsets[i as usize + 1] as usize],
                to_slice,
                depth + 1,
            );
        }
        sort_req(&mut vec[offsets[num_items - 1]..], to_slice, depth + 1);
    }

    fn radix_for_str(s: &[u8], d: usize, base: u16) -> u16 {
        if s.len() > d {
            s[d] as u16 + 1 - base
        } else {
            0
        }
    }
}

#[cfg(test)]
mod tests {

    use afsort;
    use quickcheck::QuickCheck;
    use test::Bencher;
    use std::fs::File;
    use std::io::{BufRead, BufReader};
    use rand::{self, Rng};
    use regex::Regex;
    use std::path::PathBuf;

    #[test]
    fn sorts_strings_same_as_unstable() {
        fn compare_sort(mut strings: Vec<String>) -> bool {
            let mut copy = strings.clone();
            copy.sort_unstable();
            afsort::sort(&mut strings);
            strings == copy
        }
        QuickCheck::new().tests(1000).quickcheck(
            compare_sort as
                fn(Vec<String>) -> bool,
        );
    }

    #[test]
    fn sorts_tuples_same_as_unstable() {
        fn compare_sort(mut tuples: Vec<(String, u8)>) -> bool {
            let mut copy = tuples.clone();
            copy.sort_unstable_by(|t1, t2| t1.0.cmp(&t2.0));
            afsort::sort_by(&mut tuples, |t| t.0.as_bytes());
            tuples.into_iter().map(|t| t.0).collect::<Vec<String>>() ==
                copy.into_iter().map(|t| t.0).collect::<Vec<String>>()
        }
        QuickCheck::new().tests(1000).quickcheck(
            compare_sort as
                fn(Vec<(String, u8)>) -> bool,
        );
    }

    #[bench]
    fn sort_1000_en_std(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 1_000);
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_1000_en_af(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 1_000);
        b.iter(|| afsort::sort(&mut strings.clone()))
    }

    #[bench]
    fn sort_10000_en_std(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 10_000);
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_10000_en_af(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 10_000);
        b.iter(|| afsort::sort(&mut strings.clone()))
    }

    #[bench]
    fn sort_100000_en_std(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 100_000);
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_100000_en_af(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 100_000);
        b.iter(|| afsort::sort(&mut strings.clone()))
    }

    #[bench]
    fn sort_10000_en_sorted_std(b: &mut Bencher) {
        let mut strings = strings_en(&Regex::new(r".*").unwrap(), 10_000);
        strings.sort_unstable();
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_10000_en_sorted_af(b: &mut Bencher) {
        let mut strings = strings_en(&Regex::new(r".*").unwrap(), 10_000);
        strings.sort_unstable();
        b.iter(|| afsort::sort(&mut strings.clone()))
    }

    #[bench]
    fn sort_10000_en_lower_std(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r"^[a-z]+$").unwrap(), 10000);
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_10000_en_lower_af(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r"^[a-z]+$").unwrap(), 10000);
        b.iter(|| afsort::sort(&mut strings.clone()))
    }

    fn strings_en(re: &Regex, n: usize) -> Vec<String> {
        let d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        let f = File::open(d.join("test_resources/american-english.txt")).unwrap();
        let b = BufReader::new(f);
        let mut strings = b.lines()
            .map(|l| l.unwrap())
            .filter(|l| re.is_match(l))
            .collect::<Vec<String>>();
        let mut rng = rand::thread_rng();
        rng.shuffle(&mut strings);
        strings.into_iter().take(n).collect::<Vec<String>>()
    }

}
<commit_msg>Renamed methonds to sort_unstable and added trait.<commit_after>#![feature(test)]
#[cfg(test)]
extern crate test;
#[cfg(test)]
extern crate rand;
#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
extern crate regex;

pub mod afsort {

    pub trait AFSortable {
        fn af_sort_unstable(&mut self);
    }

    impl<T> AFSortable for [T]
    where
        T: AsRef<[u8]>,
    {
        fn af_sort_unstable(&mut self) {
            sort_unstable(self);
        }
    }

    pub fn sort_unstable<'a, T>(vec: &'a mut [T])
    where
        T: AsRef<[u8]>,
    {
        sort_unstable_by(vec, (|s| s.as_ref()));
    }

    pub fn sort_unstable_by<T, F>(vec: &mut [T], to_slice: F)
    where
        F: Fn(&T) -> &[u8],
    {
        sort_req(vec, &to_slice, 0);
    }

    fn sort_req<T, F>(vec: &mut [T], to_slice: &F, depth: usize)
    where
        F: Fn(&T) -> &[u8],
    {
        if vec.len() <= 32 {
            vec.sort_unstable_by(|e1, e2| to_slice(e1).cmp(to_slice(e2)));
            return;
        }
        let mut min = u16::max_value();
        let mut max = 0u16;
        for elem in vec.iter() {
            let val = to_slice(elem);
            if val.len() > depth {
                let radix_val = val[depth] as u16;
                if radix_val < min {
                    min = radix_val;
                }
                if radix_val > max {
                    max = radix_val;
                }
            }
        }
        if min == u16::max_value() {
            return;
        }

        let num_items = (max - min + 2) as usize;
        let mut counts: Vec<usize> = vec![0usize; num_items as usize];

        for elem in vec.iter() {
            let radix_val = radix_for_str(to_slice(elem), depth, min);
            counts[radix_val as usize] += 1;
        }

        let mut sum = 0usize;
        let mut offsets: Vec<usize> = vec![0usize; num_items as usize];
        for i in 0..num_items {
            offsets[i as usize] = sum;
            sum += counts[i as usize];
        }
        let mut next_free = offsets.clone();

        let mut block = 0usize;
        let mut i = 0usize;
        while block < num_items - 1 {
            if i >= offsets[block as usize + 1] as usize {
                block += 1;
            } else {
                let radix_val = radix_for_str(to_slice(&vec[i]), depth, min);
                if radix_val == block as u16 {
                    i += 1;
                } else {
                    vec.swap(i as usize, next_free[radix_val as usize] as usize);
                    next_free[radix_val as usize] += 1;
                }
            }
        }
        for i in 0..num_items - 1 {
            sort_req(
                &mut vec[offsets[i as usize] as usize..offsets[i as usize + 1] as usize],
                to_slice,
                depth + 1,
            );
        }
        sort_req(&mut vec[offsets[num_items - 1]..], to_slice, depth + 1);
    }

    fn radix_for_str(s: &[u8], d: usize, base: u16) -> u16 {
        if s.len() > d {
            s[d] as u16 + 1 - base
        } else {
            0
        }
    }
}

#[cfg(test)]
mod tests {

    use afsort;
    use afsort::AFSortable;
    use quickcheck::QuickCheck;
    use test::Bencher;
    use std::fs::File;
    use std::io::{BufRead, BufReader};
    use rand::{self, Rng};
    use regex::Regex;
    use std::path::PathBuf;

    #[test]
    fn sorts_strings_same_as_unstable() {
        fn compare_sort(mut strings: Vec<String>) -> bool {
            let mut copy = strings.clone();
            copy.sort_unstable();
            strings.af_sort_unstable();
            strings == copy
        }
        QuickCheck::new().tests(1000).quickcheck(
            compare_sort as
                fn(Vec<String>) -> bool,
        );
    }

    #[test]
    fn sorts_tuples_same_as_unstable() {
        fn compare_sort(mut tuples: Vec<(String, u8)>) -> bool {
            let mut copy = tuples.clone();
            copy.sort_unstable_by(|t1, t2| t1.0.cmp(&t2.0));
            afsort::sort_unstable_by(&mut tuples, |t| t.0.as_bytes());
            tuples.into_iter().map(|t| t.0).collect::<Vec<String>>() ==
                copy.into_iter().map(|t| t.0).collect::<Vec<String>>()
        }
        QuickCheck::new().tests(1000).quickcheck(
            compare_sort as
                fn(Vec<(String, u8)>) -> bool,
        );
    }

    #[bench]
    fn sort_1000_en_std(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 1_000);
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_1000_en_af(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 1_000);
        b.iter(|| strings.clone().af_sort_unstable())
    }

    #[bench]
    fn sort_10000_en_std(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 10_000);
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_10000_en_af(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 10_000);
        b.iter(|| strings.clone().af_sort_unstable())
    }

    #[bench]
    fn sort_100000_en_std(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 100_000);
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_100000_en_af(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r".*").unwrap(), 100_000);
        b.iter(|| strings.clone().af_sort_unstable())
    }

    #[bench]
    fn sort_10000_en_sorted_std(b: &mut Bencher) {
        let mut strings = strings_en(&Regex::new(r".*").unwrap(), 10_000);
        strings.sort_unstable();
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_10000_en_sorted_af(b: &mut Bencher) {
        let mut strings = strings_en(&Regex::new(r".*").unwrap(), 10_000);
        strings.sort_unstable();
        b.iter(|| strings.clone().af_sort_unstable())
    }

    #[bench]
    fn sort_10000_en_lower_std(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r"^[a-z]+$").unwrap(), 10000);
        b.iter(|| strings.clone().sort_unstable())
    }

    #[bench]
    fn sort_10000_en_lower_af(b: &mut Bencher) {
        let strings = strings_en(&Regex::new(r"^[a-z]+$").unwrap(), 10000);
        b.iter(|| strings.clone().af_sort_unstable())
    }

    fn strings_en(re: &Regex, n: usize) -> Vec<String> {
        let d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        let f = File::open(d.join("test_resources/american-english.txt")).unwrap();
        let b = BufReader::new(f);
        let mut strings = b.lines()
            .map(|l| l.unwrap())
            .filter(|l| re.is_match(l))
            .collect::<Vec<String>>();
        let mut rng = rand::thread_rng();
        rng.shuffle(&mut strings);
        strings.into_iter().take(n).collect::<Vec<String>>()
    }

}
<|endoftext|>


<commit_before>
enum Order {
  Ascending,
  Descending,
  Ignore
}

enum Type<'a> {
  Null,
  Boolean,
  Int,
  Long,
  Float,
  Double,
  Bytes,
  String,
  Record(Record<'a>),
  Enum(Enum<'a>),
  Array(Array<'a>),
  Map(Map<'a>),
  Union(&'a[Box<Type<'a>>]),
  Fixed(Fixed<'a>)
}

struct Field<'a> {
  name: &'static str,
  doc: Option<String>,
  av_type: Type<'a>,
  default: &'static str,
  order: Option<Order>
}

struct Record<'a> {
    name: &'static str,
    namespace: Option<String>,
    doc: Option<String>,
    aliases: Option<&'a[String]>,
    fields: &'a[Field<'a>]
}

struct Enum<'a> {
    name: &'static str,
    namespace: Option<String>,
    doc: Option<String>,
    aliases: Option<&'a[String]>,
    symbols: &'a[String]
}

struct Array<'a> {
  items: Box<Type<'a>>
}

struct Map<'a> {
  values: &'a[String]
}

struct Fixed<'a> {
  name: &'static str,
  namespace: Option<String>,
  aliases: Option<&'a[String]>,
  size: i32
}

trait Valid {
  fn valid(&self) -> bool;
}

#[test]
#[should_panic]
fn it_works() {
  let arr: Array = Array {
    items: Box::new(Type::Null)
  };
  let field: Field = Field {
    name: "null_array",
    doc: None,
    av_type: Type::Array(arr),
    default: "null",
    order: Some(Order::Ascending)
  };
  let record: Record = Record {
    name: "record",
    namespace: None,
    doc: None,
    aliases: None,
    fields: &[field]
  };
  println!("hello world");
  assert!(false)
}
<commit_msg>implemeting schema stuff<commit_after>
enum Order {
  Ascending,
  Descending,
  Ignore
}

enum Type<'a> {
  Null,
  Boolean,
  Int,
  Long,
  Float,
  Double,
  Bytes,
  String,
  Record(Record<'a>),
  Enum(Enum<'a>),
  Array(Array<'a>),
  Map(Map<'a>),
  Union(&'a[Box<Type<'a>>]),
  Fixed(Fixed<'a>)
}

struct Field<'a> {
  name: &'a str,
  doc: Option<&'a str>,
  av_type: Type<'a>,
  default: &'a str,
  order: Option<Order>
}

struct Record<'a> {
    name: &'a str,
    namespace: Option<&'a str>,
    doc: Option<&'a str>,
    aliases: Option<&'a[&'a str]>,
    fields: &'a[Field<'a>]
}

struct Enum<'a> {
    name: &'a str,
    namespace: Option<&'a str>,
    doc: Option<&'a str>,
    aliases: Option<&'a[&'a str]>,
    symbols: &'a[&'a str]
}

struct Array<'a> {
  items: Box<Type<'a>>
}

struct Map<'a> {
  values: &'a[&'a str]
}

struct Fixed<'a> {
  name: &'a str,
  namespace: Option<&'a str>,
  aliases: Option<&'a[&'a str]>,
  size: i32
}

trait Valid {
  fn valid(&self) -> bool;
}

trait ToType {
  fn to_type(&self) -> Type;
}

impl <'a>ToType for &'a str {
  fn to_type(&self) -> Type {
    return Type::Null;
  }
}

impl <'a>Valid for Field<'a> {
  fn valid(&self) -> bool {
    match &self.av_type {
      _ => false
    }
  }
}

impl <'a>Valid for Record<'a> {
  fn valid(&self) -> bool {
    return true;
  }
}

impl <'a>Valid for Enum<'a> {
  fn valid(&self) -> bool {
    return true;
  }
}

impl <'a>Valid for Array<'a> {
  fn valid(&self) -> bool {
    return true;
  }
}

impl <'a>Valid for Map<'a> {
  fn valid(&self) -> bool {
    return true;
  }
}

impl <'a>Valid for Fixed<'a> {
  fn valid(&self) -> bool {
    return true;
  }
}

#[test]
#[should_panic]
fn it_works() {
  let arr: Array = Array {
    items: Box::new(Type::Null)
  };
  let field: Field = Field {
    name: "null_array",
    doc: None,
    av_type: Type::Array(arr),
    default: "null",
    order: Some(Order::Ascending)
  };
  let record: Record = Record {
    name: "record",
    namespace: None,
    doc: None,
    aliases: None,
    fields: &[field]
  };
  println!("hello world");
  assert!(false)
}
<|endoftext|>


<commit_before>extern crate libc;

use std;
use std::mem;
use libc::c_char;
use std::ffi::CStr;

use nom::*;

use rparser::*;

use tls_parser::tls::{TlsMessage,TlsMessageHandshake,tls_parser_many};
use tls_parser::tls_ciphers::TlsCipherSuite;
use tls_parser::tls_extensions::parse_tls_extensions;
use tls_parser::tls_states::{TlsState,tls_state_transition};

// --------------------------------------------
// example implementation: TLS parser

#[repr(u32)]
pub enum TlsParserEvents {
    HeartbeatOverflow = 1,
    InvalidState = 2,
}

pub struct TlsParserState<'a> {
    pub o: Option<&'a[u8]>,

    events: Vec<u32>,

    cipher: u16,
    state: TlsState,
}

impl<'a> TlsParserState<'a> {
    pub fn new(i: &'a[u8]) -> TlsParserState<'a> {
        TlsParserState{
            o:Some(i),
            events:Vec::new(),
            cipher:0,
            state:TlsState::None
        }
    }

}

r_declare_state_new!(r_tls_state_new,TlsParserState,b"blah");
r_declare_state_free!(r_tls_state_free,TlsParserState,{ () });

struct TlsParser;

impl<'a> RParser<TlsParserState<'a>> for TlsParser {
    fn new_state() -> TlsParserState<'a> {
        TlsParserState::new(b"blah")
    }

    fn probe(i: &[u8]) -> bool {
        if i.len() <= 2 { return false; }
        // first byte is record type (between 0x14 and 0x17, 0x16 is handhake)
        // second is TLS version major (0x3)
        // third is TLS version minor (0x0 for SSLv3, 0x1 for TLSv1.0, etc.)
        match (i[0],i[1],i[2]) {
            (0x14...0x17,0x03,0...3) => true,
            _ => false,
        }
    }

    fn parse(this: &mut TlsParserState, i: &[u8], direction: u8) -> u32 {
        debug!("[TLS->parse: direction={}, len={}]",direction,i.len());

        if i.len() == 0 {
            // Connection closed ?
            return R_STATUS_OK;
        };

        let d = tls_parser_many(i);

        let mut status = R_STATUS_OK;

        match d {
            IResult::Done(rem,p) => {
                for ref record in &p {
                    debug!("{:?}", record);
                    for msg in &record.msg {
                        if this.state == TlsState::ClientChangeCipherSpec {
                            // Ignore records from now on, they are encrypted
                            break
                        };
                        // update state machine
                        match tls_state_transition(this.state, msg) {
                            Ok(s)  => this.state = s,
                            Err(_) => {
                                this.state = TlsState::Invalid;
                                this.events.push(TlsParserEvents::InvalidState as u32);
                                status |= R_STATUS_EVENTS;
                            },
                        };

                        // extract variables
                        match *msg {
                            TlsMessage::Handshake(ref m) => {
                                match *m {
                                    TlsMessageHandshake::ClientHello(ref content) => {
                                        let blah = parse_tls_extensions(content.ext.unwrap_or(b""));
                                        debug!("ext {:?}", blah);
                                    },
                                    TlsMessageHandshake::ServerHello(ref content) => {
                                        this.cipher = content.cipher;
                                        match TlsCipherSuite::from_id(content.cipher) {
                                            Some(c) => debug!("Selected cipher: {:?}", c),
                                            _ => warn!("Unknown cipher 0x{:x}", content.cipher),
                                        };
                                        let blah = parse_tls_extensions(content.ext.unwrap_or(b""));
                                        debug!("extensions: {:?}", blah);
                                    },
                                    _ => (),
                                }
                            },
                            TlsMessage::Heartbeat(ref d) => {
                                if d.payload_len as usize > d.payload.len() {
                                    warn!("Heartbeat message with incorrect length {}. Heartbleed attempt ?",d.payload.len());
                                    this.events.push(TlsParserEvents::HeartbeatOverflow as u32);
                                    status |= R_STATUS_EVENTS;
                                }
                            },
                            _ => (),
                        }
                    }
                }
                if rem.len() > 0 { warn!("** unparsed ** {:?}",rem); };
            },
            IResult::Error(e) => error!("TLS parser reported an error: {:?}", e),
            IResult::Incomplete(e) => error!("TLS parser reported incomplete input: {:?}", e),
        };

        status
    }
}

r_implement_probe!(r_tls_probe,TlsParser);
r_implement_parse!(r_tls_parse,TlsParser);

// --------------------------------------------

#[no_mangle]
pub extern fn r_tls_get_next_event(ptr: *mut libc::c_char) -> u32
// pub extern fn r_tls_get_next_event<'a>(this: &mut TlsParserState<'a>) -> u32
{
    assert!(!ptr.is_null());
    let this: &mut TlsParserState = unsafe { mem::transmute(ptr) };
    match this.events.pop() {
        None     => 0xffffffff,
        Some(ev) => ev,
    }
}

#[no_mangle]
pub extern fn rusticata_tls_get_cipher(ptr: *mut libc::c_char) -> u32
// or
// pub extern fn rusticata_tls_get_cipher<'a>(this: &TlsParserState<'a>) -> u32
// but this gives a warning:
// warning: generic functions must be mangled, #[warn(no_mangle_generic_items)] on by default
{
    let this: &Box<TlsParserState> = unsafe { mem::transmute(ptr) };
    this.cipher as u32
}

#[no_mangle]
pub extern fn rusticata_tls_cipher_of_string(value: *const c_char) -> u32
{
    let c_str = unsafe { CStr::from_ptr(value) };
    let s = c_str.to_str().unwrap();
    match TlsCipherSuite::from_name(s) {
        Some(c) => c.id as u32,
        None    => 0,
    }
}

<commit_msg>Rewrite parser loop to handle TLS record-level fragmentation<commit_after>extern crate libc;

use std;
use std::mem;
use libc::c_char;
use std::ffi::CStr;

use nom::*;

use rparser::*;

use tls_parser::tls::{TlsMessage,TlsMessageHandshake,parse_tls_raw_record,parse_tls_record_with_header};
use tls_parser::tls_ciphers::TlsCipherSuite;
use tls_parser::tls_extensions::parse_tls_extensions;
use tls_parser::tls_states::{TlsState,tls_state_transition};

// --------------------------------------------
// example implementation: TLS parser

#[repr(u32)]
pub enum TlsParserEvents {
    HeartbeatOverflow = 1,
    InvalidState = 2,

    RecordIncomplete = 3,
    RecordWithExtraBytes = 4,
    RecordOverflow = 5,
}

pub struct TlsParserState<'a> {
    pub o: Option<&'a[u8]>,

    events: Vec<u32>,

    cipher: u16,
    state: TlsState,

    /// Handshake defragmentation buffer
    buffer: Vec<u8>,
}

impl<'a> TlsParserState<'a> {
    pub fn new(i: &'a[u8]) -> TlsParserState<'a> {
        TlsParserState{
            o:Some(i),
            events:Vec::new(),
            cipher:0,
            state:TlsState::None,
            // capacity is the amount of space allocated, which means elements can be added
            // without reallocating the vector
            buffer:Vec::with_capacity(16384),
        }
    }

    fn append_buffer<'b>(self: &mut TlsParserState<'a>, buf: &'b[u8]) {
        self.buffer.extend_from_slice(&buf);
    }

    /// Message-level TLS parsing
    fn handle_parsed_tls_msg(self: &mut TlsParserState<'a>, msg: &TlsMessage) -> u32 {
        let mut status = R_STATUS_OK;
        if self.state == TlsState::ClientChangeCipherSpec {
            // Ignore records from now on, they are encrypted
            return status;
        };
        // update state machine
        match tls_state_transition(self.state, msg) {
            Ok(s)  => self.state = s,
            Err(_) => {
                self.state = TlsState::Invalid;
                self.events.push(TlsParserEvents::InvalidState as u32);
                status |= R_STATUS_EVENTS;
            },
        };
        // extract variables
        match *msg {
            TlsMessage::Handshake(ref m) => {
                match *m {
                    TlsMessageHandshake::ClientHello(ref content) => {
                        let ext = parse_tls_extensions(content.ext.unwrap_or(b""));
                        debug!("ext {:?}", ext);
                    },
                    TlsMessageHandshake::ServerHello(ref content) => {
                        self.cipher = content.cipher;
                        match TlsCipherSuite::from_id(content.cipher) {
                            Some(c) => debug!("Selected cipher: {:?}", c),
                            _ => warn!("Unknown cipher 0x{:x}", content.cipher),
                        };
                        let ext = parse_tls_extensions(content.ext.unwrap_or(b""));
                        debug!("extensions: {:?}", ext);
                    },
                    _ => (),
                }
            },
            TlsMessage::Heartbeat(ref d) => {
                if d.payload_len as usize > d.payload.len() {
                    warn!("Heartbeat message with incorrect length {}. Heartbleed attempt ?",d.payload.len());
                    self.events.push(TlsParserEvents::HeartbeatOverflow as u32);
                    status |= R_STATUS_EVENTS;
                }
            },
            _ => (),
        }

        status
    }
}

r_declare_state_new!(r_tls_state_new,TlsParserState,b"TLS parser");
r_declare_state_free!(r_tls_state_free,TlsParserState,{ () });

struct TlsParser;

impl<'a> RParser<TlsParserState<'a>> for TlsParser {
    fn new_state() -> TlsParserState<'a> {
        TlsParserState::new(b"TLS parser")
    }

    fn probe(i: &[u8]) -> bool {
        if i.len() <= 2 { return false; }
        // first byte is record type (between 0x14 and 0x17, 0x16 is handhake)
        // second is TLS version major (0x3)
        // third is TLS version minor (0x0 for SSLv3, 0x1 for TLSv1.0, etc.)
        match (i[0],i[1],i[2]) {
            (0x14...0x17,0x03,0...3) => true,
            _ => false,
        }
    }

    fn parse(this: &mut TlsParserState, i: &[u8], direction: u8) -> u32 {
        debug!("[TLS->parse: direction={}, len={}]",direction,i.len());

        if i.len() == 0 {
            // Connection closed ?
            return R_STATUS_OK;
        };

        let mut status = R_STATUS_OK;
        let mut v : Vec<u8>;
        let mut cur_i = i;

        while cur_i.len() > 0 {
            match parse_tls_raw_record(cur_i) {
                IResult::Done(rem, ref r) => {
                    // advance next parsing slice
                    cur_i = rem;
                    // Record length must not be greater than 2^14 ([RFC5246] section 6.2.1)
                    if r.hdr.len > 16384 {
                        this.events.push(TlsParserEvents::RecordOverflow as u32);
                        status |= R_STATUS_EVENTS;
                        continue;
                    };
                    // XXX record may be compressed
                    // Check if a record is being defragmented
                    let buffer = match this.buffer.len() {
                        0 => r.data,
                        _ => {
                            v = this.buffer.split_off(0);
                            // sanity check vector length to avoid memory exhaustion
                            // maximum length may be 2^24 (handshake message)
                            if this.buffer.len() + r.data.len() > 16777216 {
                                this.events.push(TlsParserEvents::RecordOverflow as u32);
                                status |= R_STATUS_EVENTS;
                                continue;
                            };
                            v.extend_from_slice(r.data);
                            v.as_slice()
                        },
                    };
                    // do not parse if session is encrypted
                    if this.state == TlsState::ClientChangeCipherSpec {
                        continue;
                    };
                    // XXX Parse one message at a time ?
                    // Parse record contents as plaintext
                    match parse_tls_record_with_header(buffer,r.hdr.clone()) {
                        IResult::Done(rem2,ref msg_list) => {
                            for msg in msg_list {
                                status |= this.handle_parsed_tls_msg(msg);
                            };
                            if rem2.len() > 0 {
                                warn!("extra bytes in TLS record: {:?}",rem2);
                                this.events.push(TlsParserEvents::RecordWithExtraBytes as u32);
                                status |= R_STATUS_EVENTS;
                            };
                        }
                        IResult::Incomplete(_) => {
                            debug!("Defragmentation required (TLS record)");
                            // Record is fragmented
                            this.append_buffer(r.data);
                        },
                        IResult::Error(e) => { warn!("parse_tls_record_with_header failed: {:?}",e); break; },
                    };
                },
                IResult::Incomplete(_) => {
                    warn!("Fragmentation required (TCP level ?) {:?}", cur_i);
                    this.events.push(TlsParserEvents::RecordIncomplete as u32);
                    status |= R_STATUS_EVENTS;
                    break;
                },
                IResult::Error(e) => { warn!("Parsing failed: {:?}",e); break; },
            }
        };

        status
    }
}

r_implement_probe!(r_tls_probe,TlsParser);
r_implement_parse!(r_tls_parse,TlsParser);

// --------------------------------------------

#[no_mangle]
pub extern fn r_tls_get_next_event(ptr: *mut libc::c_char) -> u32
// pub extern fn r_tls_get_next_event<'a>(this: &mut TlsParserState<'a>) -> u32
{
    assert!(!ptr.is_null());
    let this: &mut TlsParserState = unsafe { mem::transmute(ptr) };
    match this.events.pop() {
        None     => 0xffffffff,
        Some(ev) => ev,
    }
}

#[no_mangle]
pub extern fn rusticata_tls_get_cipher(ptr: *mut libc::c_char) -> u32
// or
// pub extern fn rusticata_tls_get_cipher<'a>(this: &TlsParserState<'a>) -> u32
// but this gives a warning:
// warning: generic functions must be mangled, #[warn(no_mangle_generic_items)] on by default
{
    let this: &Box<TlsParserState> = unsafe { mem::transmute(ptr) };
    this.cipher as u32
}

#[no_mangle]
pub extern fn rusticata_tls_cipher_of_string(value: *const c_char) -> u32
{
    let c_str = unsafe { CStr::from_ptr(value) };
    let s = c_str.to_str().unwrap();
    match TlsCipherSuite::from_name(s) {
        Some(c) => c.id as u32,
        None    => 0,
    }
}

<|endoftext|>


<commit_before>#![feature(clone_from_slice)]
#[cfg_attr(test, feature(convert))]

extern crate ladspa;
extern crate mio;
extern crate rustc_serialize;
extern crate bincode;

mod receive;
mod transmit;
mod packet;

#[cfg(test)]
mod test;

use ladspa::PluginDescriptor;

use receive::Receiver;
use transmit::Transmitter;

const BASE_PORT: u16 = 21300;

#[no_mangle]
pub extern "C" fn get_ladspa_descriptor(index: u64) -> Option<PluginDescriptor> {
    match index {
        0 => Some(Transmitter::get_descriptor()),
        1 => Some(Receiver::get_descriptor()),
        _ => None,
    }
}
<commit_msg>actually fix warning<commit_after>#![feature(clone_from_slice)]
#![cfg_attr(test, feature(convert))]

extern crate ladspa;
extern crate mio;
extern crate rustc_serialize;
extern crate bincode;

mod receive;
mod transmit;
mod packet;

#[cfg(test)]
mod test;

use ladspa::PluginDescriptor;

use receive::Receiver;
use transmit::Transmitter;

const BASE_PORT: u16 = 21300;

#[no_mangle]
pub extern "C" fn get_ladspa_descriptor(index: u64) -> Option<PluginDescriptor> {
    match index {
        0 => Some(Transmitter::get_descriptor()),
        1 => Some(Receiver::get_descriptor()),
        _ => None,
    }
}
<|endoftext|>


<commit_before>//! # mdBook
//!
//! **mdBook** is similar to Gitbook but implemented in Rust.
//! It offers a command line interface, but can also be used as a regular crate.
//!
//! This is the API doc, but you can find a [less "low-level" documentation here](../index.html) that
//! contains information about the command line tool, format, structure etc.
//! It is also rendered with mdBook to showcase the features and default theme.
//!
//! Some reasons why you would want to use the crate (over the cli):
//!
//! - Integrate mdbook in a current project
//! - Extend the capabilities of mdBook
//! - Do some processing or test before building your book
//! - Write a new Renderer
//! - ...
//!
//! ## Example
//!
//! ```no_run
//! extern crate mdbook;
//!
//! use mdbook::MDBook;
//!
//! # #[allow(unused_variables)]
//! fn main() {
//!     let mut book =  MDBook::new("my-book")        // Path to root
//!                         .with_source("src")       // Path from root to source directory
//!                         .with_destination("book") // Path from root to output directory
//!                         .read_config()            // Parse book.toml configuration file
//!                         .expect("I don't handle configuration file error, but you should!");
//!
//!     book.build().unwrap();                        // Render the book
//! }
//! ```
//!
//! ## Implementing a new Renderer
//!
//! If you want to create a new renderer for mdBook, the only thing you have to do is to implement
//! the [Renderer trait](renderer/renderer/trait.Renderer.html)
//!
//! And then you can swap in your renderer like this:
//!
//! ```no_run
//! # extern crate mdbook;
//! #
//! # use mdbook::MDBook;
//! # use mdbook::renderer::HtmlHandlebars;
//! #
//! # #[allow(unused_variables)]
//! # fn main() {
//! #   let your_renderer = HtmlHandlebars::new();
//! #
//!     let book =  MDBook::new("my-book").set_renderer(Box::new(your_renderer));
//! # }
//! ```
//! If you make a renderer, you get the book constructed in form of `Vec<BookItems>` and you get
//! the book config in a `BookConfig` struct.
//!
//! It's your responsability to create the necessary files in the correct directories.
//!
//! ## utils
//!
//! I have regrouped some useful functions in the [utils](utils/index.html) module, like the
//! following function [`utils::fs::create_file(path:
//! &Path)`](utils/fs/fn.create_file.html)
//!
//! This function creates a file and returns it. But before creating the file it checks every directory in the path to see if it exists, and if it does not it will be created.
//!
//! Make sure to take a look at it.

#[macro_use]
extern crate serde_derive;
extern crate serde;
#[macro_use] extern crate serde_json;

extern crate handlebars;
extern crate pulldown_cmark;
extern crate regex;

#[macro_use]
extern crate log;
pub mod book;
pub mod config;
mod parse;
pub mod renderer;
pub mod theme;
pub mod utils;

pub use book::MDBook;
pub use book::BookItem;
pub use renderer::Renderer;
<commit_msg>Fix typo<commit_after>//! # mdBook
//!
//! **mdBook** is similar to Gitbook but implemented in Rust.
//! It offers a command line interface, but can also be used as a regular crate.
//!
//! This is the API doc, but you can find a [less "low-level" documentation here](../index.html) that
//! contains information about the command line tool, format, structure etc.
//! It is also rendered with mdBook to showcase the features and default theme.
//!
//! Some reasons why you would want to use the crate (over the cli):
//!
//! - Integrate mdbook in a current project
//! - Extend the capabilities of mdBook
//! - Do some processing or test before building your book
//! - Write a new Renderer
//! - ...
//!
//! ## Example
//!
//! ```no_run
//! extern crate mdbook;
//!
//! use mdbook::MDBook;
//!
//! # #[allow(unused_variables)]
//! fn main() {
//!     let mut book =  MDBook::new("my-book")        // Path to root
//!                         .with_source("src")       // Path from root to source directory
//!                         .with_destination("book") // Path from root to output directory
//!                         .read_config()            // Parse book.toml configuration file
//!                         .expect("I don't handle configuration file errors, but you should!");
//!
//!     book.build().unwrap();                        // Render the book
//! }
//! ```
//!
//! ## Implementing a new Renderer
//!
//! If you want to create a new renderer for mdBook, the only thing you have to do is to implement
//! the [Renderer trait](renderer/renderer/trait.Renderer.html)
//!
//! And then you can swap in your renderer like this:
//!
//! ```no_run
//! # extern crate mdbook;
//! #
//! # use mdbook::MDBook;
//! # use mdbook::renderer::HtmlHandlebars;
//! #
//! # #[allow(unused_variables)]
//! # fn main() {
//! #   let your_renderer = HtmlHandlebars::new();
//! #
//!     let book =  MDBook::new("my-book").set_renderer(Box::new(your_renderer));
//! # }
//! ```
//! If you make a renderer, you get the book constructed in form of `Vec<BookItems>` and you get
//! the book config in a `BookConfig` struct.
//!
//! It's your responsability to create the necessary files in the correct directories.
//!
//! ## utils
//!
//! I have regrouped some useful functions in the [utils](utils/index.html) module, like the
//! following function [`utils::fs::create_file(path:
//! &Path)`](utils/fs/fn.create_file.html)
//!
//! This function creates a file and returns it. But before creating the file it checks every directory in the path to see if it exists, and if it does not it will be created.
//!
//! Make sure to take a look at it.

#[macro_use]
extern crate serde_derive;
extern crate serde;
#[macro_use] extern crate serde_json;

extern crate handlebars;
extern crate pulldown_cmark;
extern crate regex;

#[macro_use]
extern crate log;
pub mod book;
pub mod config;
mod parse;
pub mod renderer;
pub mod theme;
pub mod utils;

pub use book::MDBook;
pub use book::BookItem;
pub use renderer::Renderer;
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

#![cfg_attr(not(test), no_std)]

//! A collection of strongly typed math tools for computer graphics with an inclination
//! towards 2d graphics and layout.
//!
//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.),
//! and tagged with a generic Unit parameter which is useful to prevent mixing
//! values from different spaces. For example it should not be legal to translate
//! a screen-space position by a world-space vector and this can be expressed using
//! the generic Unit parameter.
//!
//! This unit system is not mandatory and all structures have an alias
//! with the default unit: `UnknownUnit`.
//! for example ```default::Point2D<T>``` is equivalent to ```Point2D<T, UnknownUnit>```.
//! Client code typically creates a set of aliases for each type and doesn't need
//! to deal with the specifics of typed units further. For example:
//!
//! ```rust
//! use euclid::*;
//! pub struct ScreenSpace;
//! pub type ScreenPoint = Point2D<f32, ScreenSpace>;
//! pub type ScreenSize = Size2D<f32, ScreenSpace>;
//! pub struct WorldSpace;
//! pub type WorldPoint = Point3D<f32, WorldSpace>;
//! pub type ProjectionMatrix = Transform3D<f32, WorldSpace, ScreenSpace>;
//! // etc...
//! ```
//!
//! All euclid types are marked `#[repr(C)]` in order to facilitate exposing them to
//! foreign function interfaces (provided the underlying scalar type is also `repr(C)`).
//!
#![deny(unconditional_recursion)]

#[cfg(feature = "serde")]
extern crate serde;

#[cfg(feature = "mint")]
pub extern crate mint;
extern crate num_traits;

#[cfg(not(test))]
pub use core;
#[cfg(test)]
use std as core;

pub use crate::angle::Angle;
pub use crate::box2d::Box2D;
pub use crate::length::Length;
pub use crate::scale::Scale;
pub use crate::transform2d::Transform2D;
pub use crate::transform3d::Transform3D;
pub use crate::point::{Point2D, Point3D, point2, point3};
pub use crate::vector::{Vector2D, Vector3D, vec2, vec3};
pub use crate::vector::{BoolVector2D, BoolVector3D, bvec2, bvec3};
pub use crate::homogen::HomogeneousVector;
pub use crate::nonempty::NonEmpty;

pub use crate::rect::{rect, Rect};
pub use crate::rigid::{RigidTransform3D};
pub use crate::box3d::{box3d, Box3D};
pub use crate::translation::{Translation2D, Translation3D};
pub use crate::rotation::{Rotation2D, Rotation3D};
pub use crate::side_offsets::SideOffsets2D;
pub use crate::size::{Size2D, Size3D, size2, size3};
pub use crate::trig::Trig;

#[macro_use]
mod macros;

mod angle;
pub mod approxeq;
pub mod approxord;
mod box2d;
mod homogen;
pub mod num;
mod length;
mod point;
mod rect;
mod rigid;
mod rotation;
mod scale;
mod side_offsets;
mod size;
mod transform2d;
mod transform3d;
mod translation;
mod trig;
mod vector;
mod box3d;
mod nonempty;

/// The default unit.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnknownUnit;

pub mod default {
    //! A set of aliases for all types, tagged with the default unknown unit.

    use super::UnknownUnit;
    pub type Length<T> = super::Length<T, UnknownUnit>;
    pub type Point2D<T> = super::Point2D<T, UnknownUnit>;
    pub type Point3D<T> = super::Point3D<T, UnknownUnit>;
    pub type Vector2D<T> = super::Vector2D<T, UnknownUnit>;
    pub type Vector3D<T> = super::Vector3D<T, UnknownUnit>;
    pub type HomogeneousVector<T> = super::HomogeneousVector<T, UnknownUnit>;
    pub type Size2D<T> = super::Size2D<T, UnknownUnit>;
    pub type Size3D<T> = super::Size3D<T, UnknownUnit>;
    pub type Rect<T> = super::Rect<T, UnknownUnit>;
    pub type Box2D<T> = super::Box2D<T, UnknownUnit>;
    pub type Box3D<T> = super::Box3D<T, UnknownUnit>;
    pub type SideOffsets2D<T> = super::SideOffsets2D<T, UnknownUnit>;
    pub type Transform2D<T> = super::Transform2D<T, UnknownUnit, UnknownUnit>;
    pub type Transform3D<T> = super::Transform3D<T, UnknownUnit, UnknownUnit>;
    pub type Rotation2D<T> = super::Rotation2D<T, UnknownUnit, UnknownUnit>;
    pub type Rotation3D<T> = super::Rotation3D<T, UnknownUnit, UnknownUnit>;
    pub type Translation2D<T> = super::Translation2D<T, UnknownUnit, UnknownUnit>;
    pub type Translation3D<T> = super::Translation3D<T, UnknownUnit, UnknownUnit>;
    pub type Scale<T> = super::Scale<T, UnknownUnit, UnknownUnit>;
    pub type RigidTransform3D<T> = super::RigidTransform3D<T, UnknownUnit, UnknownUnit>;
}
<commit_msg>Remove the explicit `extern crate`s<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

#![cfg_attr(not(test), no_std)]

//! A collection of strongly typed math tools for computer graphics with an inclination
//! towards 2d graphics and layout.
//!
//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.),
//! and tagged with a generic Unit parameter which is useful to prevent mixing
//! values from different spaces. For example it should not be legal to translate
//! a screen-space position by a world-space vector and this can be expressed using
//! the generic Unit parameter.
//!
//! This unit system is not mandatory and all structures have an alias
//! with the default unit: `UnknownUnit`.
//! for example ```default::Point2D<T>``` is equivalent to ```Point2D<T, UnknownUnit>```.
//! Client code typically creates a set of aliases for each type and doesn't need
//! to deal with the specifics of typed units further. For example:
//!
//! ```rust
//! use euclid::*;
//! pub struct ScreenSpace;
//! pub type ScreenPoint = Point2D<f32, ScreenSpace>;
//! pub type ScreenSize = Size2D<f32, ScreenSpace>;
//! pub struct WorldSpace;
//! pub type WorldPoint = Point3D<f32, WorldSpace>;
//! pub type ProjectionMatrix = Transform3D<f32, WorldSpace, ScreenSpace>;
//! // etc...
//! ```
//!
//! All euclid types are marked `#[repr(C)]` in order to facilitate exposing them to
//! foreign function interfaces (provided the underlying scalar type is also `repr(C)`).
//!
#![deny(unconditional_recursion)]

#[cfg(not(test))]
pub use core;
#[cfg(test)]
use std as core;

pub use crate::angle::Angle;
pub use crate::box2d::Box2D;
pub use crate::length::Length;
pub use crate::scale::Scale;
pub use crate::transform2d::Transform2D;
pub use crate::transform3d::Transform3D;
pub use crate::point::{Point2D, Point3D, point2, point3};
pub use crate::vector::{Vector2D, Vector3D, vec2, vec3};
pub use crate::vector::{BoolVector2D, BoolVector3D, bvec2, bvec3};
pub use crate::homogen::HomogeneousVector;
pub use crate::nonempty::NonEmpty;

pub use crate::rect::{rect, Rect};
pub use crate::rigid::{RigidTransform3D};
pub use crate::box3d::{box3d, Box3D};
pub use crate::translation::{Translation2D, Translation3D};
pub use crate::rotation::{Rotation2D, Rotation3D};
pub use crate::side_offsets::SideOffsets2D;
pub use crate::size::{Size2D, Size3D, size2, size3};
pub use crate::trig::Trig;

#[macro_use]
mod macros;

mod angle;
pub mod approxeq;
pub mod approxord;
mod box2d;
mod homogen;
pub mod num;
mod length;
mod point;
mod rect;
mod rigid;
mod rotation;
mod scale;
mod side_offsets;
mod size;
mod transform2d;
mod transform3d;
mod translation;
mod trig;
mod vector;
mod box3d;
mod nonempty;

/// The default unit.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnknownUnit;

pub mod default {
    //! A set of aliases for all types, tagged with the default unknown unit.

    use super::UnknownUnit;
    pub type Length<T> = super::Length<T, UnknownUnit>;
    pub type Point2D<T> = super::Point2D<T, UnknownUnit>;
    pub type Point3D<T> = super::Point3D<T, UnknownUnit>;
    pub type Vector2D<T> = super::Vector2D<T, UnknownUnit>;
    pub type Vector3D<T> = super::Vector3D<T, UnknownUnit>;
    pub type HomogeneousVector<T> = super::HomogeneousVector<T, UnknownUnit>;
    pub type Size2D<T> = super::Size2D<T, UnknownUnit>;
    pub type Size3D<T> = super::Size3D<T, UnknownUnit>;
    pub type Rect<T> = super::Rect<T, UnknownUnit>;
    pub type Box2D<T> = super::Box2D<T, UnknownUnit>;
    pub type Box3D<T> = super::Box3D<T, UnknownUnit>;
    pub type SideOffsets2D<T> = super::SideOffsets2D<T, UnknownUnit>;
    pub type Transform2D<T> = super::Transform2D<T, UnknownUnit, UnknownUnit>;
    pub type Transform3D<T> = super::Transform3D<T, UnknownUnit, UnknownUnit>;
    pub type Rotation2D<T> = super::Rotation2D<T, UnknownUnit, UnknownUnit>;
    pub type Rotation3D<T> = super::Rotation3D<T, UnknownUnit, UnknownUnit>;
    pub type Translation2D<T> = super::Translation2D<T, UnknownUnit, UnknownUnit>;
    pub type Translation3D<T> = super::Translation3D<T, UnknownUnit, UnknownUnit>;
    pub type Scale<T> = super::Scale<T, UnknownUnit, UnknownUnit>;
    pub type RigidTransform3D<T> = super::RigidTransform3D<T, UnknownUnit, UnknownUnit>;
}
<|endoftext|>


<commit_before>#![feature(std_misc)]
use std::sync::Semaphore;
use std::thread;

pub struct ScopedThreadPool {
    sem: std::sync::Semaphore,
}

impl ScopedThreadPool {
    pub fn new(size: u32) -> ScopedThreadPool {
       ScopedThreadPool { sem: std::sync::Semaphore::new(size as isize) /* as safe here? */}
    }

    pub fn execute<'a, T, F>(&'a self, func: F) -> std::thread::JoinGuard<'a, T>
        where T: Send + 'a, F: FnOnce() -> T, F: Send + 'a
    {
       let guard = self.sem.access();
       thread::scoped(move || {
           let _guard = guard;
           func()
       })
    }
}

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

    #[test]
    // TODO find a better way to test than have someone pay attention to the commandline...
    fn it_works() {
        let pool = ScopedThreadPool::new(2);

        let mut guards: Vec<thread::JoinGuard<()>> = Vec::new();

        for _ in (0..3) {
            guards.push(pool.execute(move || {
                thread::sleep_ms(1000);
                println!("Things are happening!");
            }));
        }
    }
}
<commit_msg>attempt to keep the guards with the pool<commit_after>#![feature(std_misc)]
#![feature(scoped)]
use std::sync::Semaphore;
use std::thread;

pub struct ScopedThreadPool<'a> {
    sem: std::sync::Semaphore,
    guards: std::vec::Vec<thread::JoinGuard<'a, ()>>,
}

impl<'a> ScopedThreadPool<'a> {
    pub fn new(size: u32) -> ScopedThreadPool<'a> {
        ScopedThreadPool {
            sem: std::sync::Semaphore::new(size as isize), /* as safe here? */
            guards: Vec::new(),
        }
    }

    pub fn execute<F>(&'a mut self, func: F)
        where F: FnOnce() -> (), F: Send + 'a
    {
        let pool_guard = self.sem.access();
        let join_guard = thread::scoped(move || {
            let _pg = pool_guard;
            func()
        });
        self.guards.push(join_guard);
    }
}

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

    #[test]
    // TODO find a better way to test than have someone pay attention to the commandline...
    fn it_works() {
        let mut pool = ScopedThreadPool::new(2);

        for _ in (0..3) {
           pool.execute(move || {
                thread::sleep_ms(1000);
                println!("Things are happening!");
            });
        }
    }
}
<|endoftext|>