<commit_before>use std::cmp::min;
use std::rc::Rc;
use std::cell::Cell;

use Cursive;
use With;
use menu::MenuTree;
use direction::Direction;
use view::{Position, ScrollBase, View};
use views::{IdView, MenuPopup};
use align::{Align, HAlign, VAlign};
use event::{Callback, Event, EventResult, Key};
use theme::ColorStyle;
use vec::Vec2;
use Printer;

use unicode_width::UnicodeWidthStr;

/// View to select an item among a list.
///
/// It contains a list of values of type T, with associated labels.
///
/// # Examples
///
/// ```
/// # extern crate cursive;
/// # use cursive::prelude::*;
/// # use cursive::align::HAlign;
/// # fn main() {
/// let mut time_select = SelectView::new().h_align(HAlign::Center);
/// time_select.add_item("Short", 1);
/// time_select.add_item("Medium", 5);
/// time_select.add_item("Long", 10);
///
/// time_select.set_on_select(|s, time| {
///     s.pop_layer();
///     let text = format!("You will wait for {} minutes...", time);
///     s.add_layer(Dialog::new(TextView::new(&text))
///                     .button("Quit", |s| s.quit()));
/// });
///
/// let mut siv = Cursive::new();
/// siv.add_layer(Dialog::new(time_select)
///                 .title("How long is your wait?"));
/// # }
///
/// ```
pub struct SelectView<T = String> {
    items: Vec<Item<T>>,
    enabled: bool,
    // the focus needs to be manipulable from callbacks
    focus: Rc<Cell<usize>>,
    scrollbase: ScrollBase,
    // This is a custom callback to include a &T
    select_cb: Option<Rc<Fn(&mut Cursive, &T)>>,
    align: Align,
    // `true` if we show a one-line view, with popup on selection.
    popup: bool,
    // We need the last offset to place the popup window
    // We "cache" it during the draw, so we need interior mutability.
    last_offset: Cell<Vec2>,
    last_size: Vec2,
}

impl<T: 'static> SelectView<T> {
    /// Creates a new empty SelectView.
    pub fn new() -> Self {
        SelectView {
            items: Vec::new(),
            enabled: true,
            focus: Rc::new(Cell::new(0)),
            scrollbase: ScrollBase::new(),
            select_cb: None,
            align: Align::top_left(),
            popup: false,
            last_offset: Cell::new(Vec2::zero()),
            last_size: Vec2::zero(),
        }
    }

    /// Turns `self` into a popup select view.
    ///
    /// Chainable variant.
    pub fn popup(self) -> Self {
        self.with(|s| s.set_popup(true))
    }

    /// Turns `self` into a popup select view.
    pub fn set_popup(&mut self, popup: bool) {
        self.popup = popup;
    }

    /// Disables this view.
    ///
    /// A disabled view cannot be selected.
    pub fn disable(&mut self) {
        self.enabled = false;
    }

    /// Disables this view.
    ///
    /// Chainable variant.
    pub fn disabled(self) -> Self {
        self.with(Self::disable)
    }

    /// Re-enables this view.
    pub fn enable(&mut self) {
        self.enabled = true;
    }

    /// Enable or disable this view.
    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
    }

    /// Returns `true` if this view is enabled.
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// (When ENTER is pressed on an item).
    pub fn set_on_select<F>(&mut self, cb: F)
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.select_cb = Some(Rc::new(cb));
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// (When ENTER is pressed on an item).
    ///
    /// Chainable variant.
    pub fn on_select<F>(mut self, cb: F) -> Self
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.set_on_select(cb);

        self
    }

    /// Sets the alignment for this view.
    pub fn align(mut self, align: Align) -> Self {
        self.align = align;

        self
    }

    /// Sets the vertical alignment for this view.
    /// (If the view is given too much space vertically.)
    pub fn v_align(mut self, v: VAlign) -> Self {
        self.align.v = v;

        self
    }

    /// Sets the horizontal alignment for this view.
    pub fn h_align(mut self, h: HAlign) -> Self {
        self.align.h = h;

        self
    }

    /// Returns the value of the currently selected item.
    ///
    /// Panics if the list is empty.
    pub fn selection(&self) -> Rc<T> {
        self.items[self.focus()].value.clone()
    }

    /// Adds a item to the list, with given label and value.
    pub fn add_item(&mut self, label: &str, value: T) {
        self.items.push(Item::new(label, value));
    }

    /// Chainable variant of add_item
    pub fn item(mut self, label: &str, value: T) -> Self {
        self.add_item(label, value);

        self
    }

    /// Wraps this view into an IdView with the given id.
    pub fn with_id(self, label: &str) -> IdView<Self> {
        IdView::new(label, self)
    }

    fn draw_item(&self, printer: &Printer, i: usize) {
        let l = self.items[i].label.width();
        let x = self.align.h.get_offset(l, printer.size.x);
        printer.print_hline((0, 0), x, " ");
        printer.print((x, 0), &self.items[i].label);
        if l < printer.size.x {
            printer.print_hline((x + l, 0), printer.size.x - l - x, " ");
        }
    }

    fn focus(&self) -> usize {
        self.focus.get()
    }

    fn focus_up(&mut self, n: usize) {
        let focus = self.focus();
        let n = min(focus, n);
        self.focus.set(focus - n);
    }

    fn focus_down(&mut self, n: usize) {
        let focus = min(self.focus() + n, self.items.len());
        self.focus.set(focus);
    }
}

impl SelectView<String> {
    /// Convenient method to use the label as value.
    pub fn add_item_str(&mut self, label: &str) {
        self.add_item(label, label.to_string());
    }

    /// Chainable variant of add_item_str
    pub fn item_str(self, label: &str) -> Self {
        self.item(label, label.to_string())
    }
}

impl<T: 'static> View for SelectView<T> {
    fn draw(&self, printer: &Printer) {
        self.last_offset.set(printer.offset);

        if self.popup {
            let style = if !self.enabled {
                ColorStyle::Secondary
            } else if !printer.focused {
                ColorStyle::Primary
            } else {
                ColorStyle::Highlight
            };
            let x = printer.size.x;

            printer.with_color(style, |printer| {
                // Prepare the entire background
                printer.print_hline((1, 0), x - 1, " ");
                // Draw the borders
                printer.print((0, 0), "<");
                printer.print((x - 1, 0), ">");

                let label = &self.items[self.focus()].label;

                // And center the text?
                let offset = HAlign::Center.get_offset(label.len(), x);

                printer.print((offset, 0), label);
            });
        } else {

            let h = self.items.len();
            let offset = self.align.v.get_offset(h, printer.size.y);
            let printer =
                &printer.sub_printer(Vec2::new(0, offset), printer.size, true);

            self.scrollbase.draw(printer, |printer, i| {
                printer.with_selection(i == self.focus(), |printer| {
                    if i != self.focus() && !self.enabled {
                        printer.with_color(ColorStyle::Secondary, |printer| {
                            self.draw_item(printer, i)
                        });
                    } else {
                        self.draw_item(printer, i);
                    }
                });
            });
        }
    }

    fn get_min_size(&mut self, req: Vec2) -> Vec2 {
        // Items here are not compressible.
        // So no matter what the horizontal requirements are,
        // we'll still return our longest item.
        let w = self.items
            .iter()
            .map(|item| item.label.width())
            .max()
            .unwrap_or(1);
        if self.popup {
            Vec2::new(w + 2, 1)
        } else {
            let h = self.items.len();

            let scrolling = req.y < h;

            // Add 2 spaces for the scrollbar if we need
            let w = if scrolling {
                w + 2
            } else {
                w
            };

            Vec2::new(w, h)
        }
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        if self.popup {
            match event {
                Event::Key(Key::Enter) => {
                    // Build a shallow menu tree to mimick the items array.
                    // TODO: cache it?
                    let mut tree = MenuTree::new();
                    for (i, item) in self.items.iter().enumerate() {
                        let focus = self.focus.clone();
                        let select_cb = self.select_cb.as_ref().cloned();
                        let value = item.value.clone();
                        tree.add_leaf(&item.label, move |s| {
                            focus.set(i);
                            if let Some(ref select_cb) = select_cb {
                                select_cb(s, &value);
                            }
                        });
                    }
                    // Let's keep the tree around,
                    // the callback will want to use it.
                    let tree = Rc::new(tree);

                    let focus = self.focus();
                    // This is the offset for the label text.
                    // We'll want to show the popup so that the text matches.
                    // It'll be soo cool.
                    let text_offset =
                        (self.last_size.x - self.items[focus].label.len()) / 2;
                    // The total offset for the window is:
                    // * the last absolute offset at which we drew this view
                    // * shifted to the top of the focus (so the line matches)
                    // * shifted to the right of the text offset
                    // * shifted top-left of the border+padding of the popup
                    let offset = self.last_offset.get() - (0, focus) +
                                 (text_offset, 0) -
                                 (2, 1);
                    // And now, we can return the callback.
                    EventResult::with_cb(move |s| {
                        // The callback will want to work with a fresh Rc
                        let tree = tree.clone();
                        // We'll relativise the absolute position,
                        // So that we are locked to the parent view.
                        // A nice effect is that window resizes will keep both
                        // layers together.
                        let current_offset = s.screen().offset();
                        let offset = offset - current_offset;
                        // And finally, put the view in view!
                        s.screen_mut()
                            .add_layer_at(Position::parent(offset),
                                          MenuPopup::new(tree).focus(focus));
                    })
                }
                _ => EventResult::Ignored,
            }
        } else {
            match event {
                Event::Key(Key::Up) if self.focus() > 0 => self.focus_up(1),
                Event::Key(Key::Down) if self.focus() + 1 <
                                         self.items.len() => {
                    self.focus_down(1)
                }
                Event::Key(Key::PageUp) => self.focus_up(10),
                Event::Key(Key::PageDown) => self.focus_down(10),
                Event::Key(Key::Home) => self.focus.set(0),
                Event::Key(Key::End) => self.focus.set(self.items.len() - 1),
                Event::Key(Key::Enter) if self.select_cb.is_some() => {
                    let cb = self.select_cb.as_ref().unwrap().clone();
                    let v = self.selection();
                    // We return a Callback Rc<|s| cb(s, &*v)>
                    return EventResult::Consumed(Some(Callback::from_fn(move |s| {
                        cb(s, &*v)
                    })));
                }
                Event::Char(c) => {
                    // Starting from the current focus,
                    // find the first item that match the char.
                    // Cycle back to the beginning of
                    // the list when we reach the end.
                    // This is achieved by chaining twice the iterator
                    let iter = self.items.iter().chain(self.items.iter());
                    if let Some((i, _)) = iter.enumerate()
                        .skip(self.focus() + 1)
                        .find(|&(_, item)| item.label.starts_with(c)) {
                        // Apply modulo in case we have a hit
                        // from the chained iterator
                        self.focus.set(i % self.items.len());
                    }
                }
                _ => return EventResult::Ignored,
            }
            let focus = self.focus();
            self.scrollbase.scroll_to(focus);
            EventResult::Consumed(None)
        }
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.enabled && !self.items.is_empty()
    }

    fn layout(&mut self, size: Vec2) {
        self.last_size = size;

        if !self.popup {
            self.scrollbase.set_heights(size.y, self.items.len());
        }
    }
}

struct Item<T> {
    label: String,
    value: Rc<T>,
}

impl<T> Item<T> {
    fn new(label: &str, value: T) -> Self {
        Item {
            label: label.to_string(),
            value: Rc::new(value),
        }
    }
}

<commit_msg>Add `on_select` to SelectView<commit_after>use std::cmp::min;
use std::rc::Rc;
use std::cell::Cell;

use Cursive;
use With;
use menu::MenuTree;
use direction::Direction;
use view::{Position, ScrollBase, View};
use views::{IdView, MenuPopup};
use align::{Align, HAlign, VAlign};
use event::{Callback, Event, EventResult, Key};
use theme::ColorStyle;
use vec::Vec2;
use Printer;

use unicode_width::UnicodeWidthStr;

/// View to select an item among a list.
///
/// It contains a list of values of type T, with associated labels.
///
/// # Examples
///
/// ```
/// # extern crate cursive;
/// # use cursive::prelude::*;
/// # use cursive::align::HAlign;
/// # fn main() {
/// let mut time_select = SelectView::new().h_align(HAlign::Center);
/// time_select.add_item("Short", 1);
/// time_select.add_item("Medium", 5);
/// time_select.add_item("Long", 10);
///
/// time_select.set_on_submit(|s, time| {
///     s.pop_layer();
///     let text = format!("You will wait for {} minutes...", time);
///     s.add_layer(Dialog::new(TextView::new(&text))
///                     .button("Quit", |s| s.quit()));
/// });
///
/// let mut siv = Cursive::new();
/// siv.add_layer(Dialog::new(time_select)
///                 .title("How long is your wait?"));
/// # }
///
/// ```
pub struct SelectView<T = String> {
    items: Vec<Item<T>>,
    enabled: bool,
    // the focus needs to be manipulable from callbacks
    focus: Rc<Cell<usize>>,
    scrollbase: ScrollBase,
    // This is a custom callback to include a &T.
    // It will be called whenever "Enter" is pressed.
    on_submit: Option<Rc<Fn(&mut Cursive, &T)>>,
    // This callback is called when the selection is changed.
    on_select: Option<Rc<Fn(&mut Cursive, &T)>>,
    align: Align,
    // `true` if we show a one-line view, with popup on selection.
    popup: bool,
    // We need the last offset to place the popup window
    // We "cache" it during the draw, so we need interior mutability.
    last_offset: Cell<Vec2>,
    last_size: Vec2,
}

impl<T: 'static> SelectView<T> {
    /// Creates a new empty SelectView.
    pub fn new() -> Self {
        SelectView {
            items: Vec::new(),
            enabled: true,
            focus: Rc::new(Cell::new(0)),
            scrollbase: ScrollBase::new(),
            on_select: None,
            on_submit: None,
            align: Align::top_left(),
            popup: false,
            last_offset: Cell::new(Vec2::zero()),
            last_size: Vec2::zero(),
        }
    }

    /// Turns `self` into a popup select view.
    ///
    /// Chainable variant.
    pub fn popup(self) -> Self {
        self.with(|s| s.set_popup(true))
    }

    /// Turns `self` into a popup select view.
    pub fn set_popup(&mut self, popup: bool) {
        self.popup = popup;
    }

    /// Disables this view.
    ///
    /// A disabled view cannot be selected.
    pub fn disable(&mut self) {
        self.enabled = false;
    }

    /// Disables this view.
    ///
    /// Chainable variant.
    pub fn disabled(self) -> Self {
        self.with(Self::disable)
    }

    /// Re-enables this view.
    pub fn enable(&mut self) {
        self.enabled = true;
    }

    /// Enable or disable this view.
    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
    }

    /// Returns `true` if this view is enabled.
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }

    /// Sets a callback to be used when an item is selected.
    pub fn set_on_select<F>(&mut self, cb: F)
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.on_select = Some(Rc::new(cb));
    }

    /// Sets a callback to be used when an item is selected.
    ///
    ///
    /// Chainable variant.
    pub fn on_select<F>(self, cb: F) -> Self
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.with(|s| s.set_on_select(cb))
    }

    /// Sets a callback to be used when `<Enter>` is pressed.
    ///
    /// The item currently selected will be given to the callback.
    pub fn set_on_submit<F>(&mut self, cb: F)
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.on_submit = Some(Rc::new(cb));
    }

    /// Sets a callback to be used when `<Enter>` is pressed.
    ///
    /// The item currently selected will be given to the callback.
    ///
    /// Chainable variant.
    pub fn on_submit<F>(self, cb: F) -> Self
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.with(|s| s.set_on_submit(cb))
    }

    /// Sets the alignment for this view.
    pub fn align(mut self, align: Align) -> Self {
        self.align = align;

        self
    }

    /// Sets the vertical alignment for this view.
    /// (If the view is given too much space vertically.)
    pub fn v_align(mut self, v: VAlign) -> Self {
        self.align.v = v;

        self
    }

    /// Sets the horizontal alignment for this view.
    pub fn h_align(mut self, h: HAlign) -> Self {
        self.align.h = h;

        self
    }

    /// Returns the value of the currently selected item.
    ///
    /// Panics if the list is empty.
    pub fn selection(&self) -> Rc<T> {
        self.items[self.focus()].value.clone()
    }

    /// Adds a item to the list, with given label and value.
    pub fn add_item(&mut self, label: &str, value: T) {
        self.items.push(Item::new(label, value));
    }

    /// Chainable variant of add_item
    pub fn item(mut self, label: &str, value: T) -> Self {
        self.add_item(label, value);

        self
    }

    /// Wraps this view into an IdView with the given id.
    pub fn with_id(self, label: &str) -> IdView<Self> {
        IdView::new(label, self)
    }

    fn draw_item(&self, printer: &Printer, i: usize) {
        let l = self.items[i].label.width();
        let x = self.align.h.get_offset(l, printer.size.x);
        printer.print_hline((0, 0), x, " ");
        printer.print((x, 0), &self.items[i].label);
        if l < printer.size.x {
            printer.print_hline((x + l, 0), printer.size.x - l - x, " ");
        }
    }

    fn focus(&self) -> usize {
        self.focus.get()
    }

    fn focus_up(&mut self, n: usize) {
        let focus = self.focus();
        let n = min(focus, n);
        self.focus.set(focus - n);
    }

    fn focus_down(&mut self, n: usize) {
        let focus = min(self.focus() + n, self.items.len());
        self.focus.set(focus);
    }
}

impl SelectView<String> {
    /// Convenient method to use the label as value.
    pub fn add_item_str(&mut self, label: &str) {
        self.add_item(label, label.to_string());
    }

    /// Chainable variant of add_item_str
    pub fn item_str(self, label: &str) -> Self {
        self.item(label, label.to_string())
    }
}

impl<T: 'static> View for SelectView<T> {
    fn draw(&self, printer: &Printer) {
        self.last_offset.set(printer.offset);

        if self.popup {
            let style = if !self.enabled {
                ColorStyle::Secondary
            } else if !printer.focused {
                ColorStyle::Primary
            } else {
                ColorStyle::Highlight
            };
            let x = printer.size.x;

            printer.with_color(style, |printer| {
                // Prepare the entire background
                printer.print_hline((1, 0), x - 1, " ");
                // Draw the borders
                printer.print((0, 0), "<");
                printer.print((x - 1, 0), ">");

                let label = &self.items[self.focus()].label;

                // And center the text?
                let offset = HAlign::Center.get_offset(label.len(), x);

                printer.print((offset, 0), label);
            });
        } else {

            let h = self.items.len();
            let offset = self.align.v.get_offset(h, printer.size.y);
            let printer =
                &printer.sub_printer(Vec2::new(0, offset), printer.size, true);

            self.scrollbase.draw(printer, |printer, i| {
                printer.with_selection(i == self.focus(), |printer| {
                    if i != self.focus() && !self.enabled {
                        printer.with_color(ColorStyle::Secondary, |printer| {
                            self.draw_item(printer, i)
                        });
                    } else {
                        self.draw_item(printer, i);
                    }
                });
            });
        }
    }

    fn get_min_size(&mut self, req: Vec2) -> Vec2 {
        // Items here are not compressible.
        // So no matter what the horizontal requirements are,
        // we'll still return our longest item.
        let w = self.items
            .iter()
            .map(|item| item.label.width())
            .max()
            .unwrap_or(1);
        if self.popup {
            Vec2::new(w + 2, 1)
        } else {
            let h = self.items.len();

            let scrolling = req.y < h;

            // Add 2 spaces for the scrollbar if we need
            let w = if scrolling {
                w + 2
            } else {
                w
            };

            Vec2::new(w, h)
        }
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        if self.popup {
            match event {
                Event::Key(Key::Enter) => {
                    // Build a shallow menu tree to mimick the items array.
                    // TODO: cache it?
                    let mut tree = MenuTree::new();
                    for (i, item) in self.items.iter().enumerate() {
                        let focus = self.focus.clone();
                        let on_submit = self.on_submit.as_ref().cloned();
                        let value = item.value.clone();
                        tree.add_leaf(&item.label, move |s| {
                            focus.set(i);
                            if let Some(ref on_submit) = on_submit {
                                on_submit(s, &value);
                            }
                        });
                    }
                    // Let's keep the tree around,
                    // the callback will want to use it.
                    let tree = Rc::new(tree);

                    let focus = self.focus();
                    // This is the offset for the label text.
                    // We'll want to show the popup so that the text matches.
                    // It'll be soo cool.
                    let text_offset =
                        (self.last_size.x - self.items[focus].label.len()) / 2;
                    // The total offset for the window is:
                    // * the last absolute offset at which we drew this view
                    // * shifted to the top of the focus (so the line matches)
                    // * shifted to the right of the text offset
                    // * shifted top-left of the border+padding of the popup
                    let offset = self.last_offset.get() - (0, focus) +
                                 (text_offset, 0) -
                                 (2, 1);
                    // And now, we can return the callback.
                    EventResult::with_cb(move |s| {
                        // The callback will want to work with a fresh Rc
                        let tree = tree.clone();
                        // We'll relativise the absolute position,
                        // So that we are locked to the parent view.
                        // A nice effect is that window resizes will keep both
                        // layers together.
                        let current_offset = s.screen().offset();
                        let offset = offset - current_offset;
                        // And finally, put the view in view!
                        s.screen_mut()
                            .add_layer_at(Position::parent(offset),
                                          MenuPopup::new(tree).focus(focus));
                    })
                }
                _ => EventResult::Ignored,
            }
        } else {
            match event {
                Event::Key(Key::Up) if self.focus() > 0 => self.focus_up(1),
                Event::Key(Key::Down) if self.focus() + 1 <
                                         self.items.len() => {
                    self.focus_down(1)
                }
                Event::Key(Key::PageUp) => self.focus_up(10),
                Event::Key(Key::PageDown) => self.focus_down(10),
                Event::Key(Key::Home) => self.focus.set(0),
                Event::Key(Key::End) => self.focus.set(self.items.len() - 1),
                Event::Key(Key::Enter) if self.on_submit.is_some() => {
                    let cb = self.on_submit.clone().unwrap();
                    let v = self.selection();
                    // We return a Callback Rc<|s| cb(s, &*v)>
                    return EventResult::Consumed(Some(Callback::from_fn(move |s| {
                        cb(s, &v)
                    })));
                }
                Event::Char(c) => {
                    // Starting from the current focus,
                    // find the first item that match the char.
                    // Cycle back to the beginning of
                    // the list when we reach the end.
                    // This is achieved by chaining twice the iterator
                    let iter = self.items.iter().chain(self.items.iter());
                    if let Some((i, _)) = iter.enumerate()
                        .skip(self.focus() + 1)
                        .find(|&(_, item)| item.label.starts_with(c)) {
                        // Apply modulo in case we have a hit
                        // from the chained iterator
                        self.focus.set(i % self.items.len());
                    }
                }
                _ => return EventResult::Ignored,
            }
            let focus = self.focus();
            self.scrollbase.scroll_to(focus);

            EventResult::Consumed(self.on_select.clone().map(|cb| {
                let v = self.selection();
                Callback::from_fn(move |s| cb(s, &v))
            }))
        }
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.enabled && !self.items.is_empty()
    }

    fn layout(&mut self, size: Vec2) {
        self.last_size = size;

        if !self.popup {
            self.scrollbase.set_heights(size.y, self.items.len());
        }
    }
}

struct Item<T> {
    label: String,
    value: Rc<T>,
}

impl<T> Item<T> {
    fn new(label: &str, value: T) -> Self {
        Item {
            label: label.to_string(),
            value: Rc::new(value),
        }
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;

use crossbeam_deque::{self as deque, Pop, Steal, Stealer, Worker};
use threadpool::ThreadPool;

use gc::root::Slot;
use gc::{Address, Region};

pub fn start(rootset: &[Slot], heap: Region, perm: Region, number_workers: usize) {
    let mut workers = Vec::with_capacity(number_workers);
    let mut stealers = Vec::with_capacity(number_workers);

    for _ in 0..number_workers {
        let (w, s) = deque::lifo();
        workers.push(w);
        stealers.push(s);
    }

    for root in rootset {
        let root_ptr = root.get();

        if heap.contains(root_ptr) {
            let root_obj = root_ptr.to_mut_obj();

            if !root_obj.header().is_marked_non_atomic() {
                root_obj.header_mut().mark_non_atomic();
                workers[0].push(Segment::with(root_ptr));
            }
        } else {
            debug_assert!(root_ptr.is_null() || perm.contains(root_ptr));
        }
    }

    let pool = ThreadPool::with_name("gc-worker".to_string(), number_workers);
    let nworkers_stage = Arc::new(AtomicUsize::new(number_workers));

    for (task_id, worker) in workers.into_iter().enumerate() {
        let heap_region = heap.clone();
        let perm_region = perm.clone();

        let stealers = stealers.clone();
        let nworkers_stage = nworkers_stage.clone();

        pool.execute(move || {
            let mut local_segment = Segment::new();

            loop {
                let object_addr = if !local_segment.is_empty() {
                    local_segment.pop().expect("should be non-empty")
                } else {
                    if let Some(segment) = pop(task_id, &worker, &stealers) {
                        local_segment = segment;
                        continue;
                    } else if try_terminate(&nworkers_stage) {
                        break;
                    } else {
                        continue;
                    }
                };

                let object = object_addr.to_mut_obj();

                object.visit_reference_fields(|field| {
                    let field_addr = field.get();

                    if heap_region.contains(field_addr) {
                        let field_obj = field_addr.to_mut_obj();

                        if field_obj.header().try_mark() {
                            if local_segment.has_capacity() {
                                local_segment.push(field_addr);
                            } else {
                                let new_local_segment = Segment::with(field_addr);
                                let old_local_segment =
                                    mem::replace(&mut local_segment, new_local_segment);
                                worker.push(old_local_segment);
                            }
                        }
                    } else {
                        debug_assert!(field_addr.is_null() || perm_region.contains(field_addr));
                    }
                });
            }
        });
    }

    pool.join();
}

fn pop(task_id: usize, worker: &Worker<Segment>, stealers: &[Stealer<Segment>]) -> Option<Segment> {
    loop {
        match worker.pop() {
            Pop::Empty => break,
            Pop::Data(segment) => {
                return Some(segment)
            }
            Pop::Retry => continue,
        }
    }

    for stealer in stealers {
        loop {
            match stealer.steal() {
                Steal::Empty => break,
                Steal::Data(segment) => {
                    return Some(segment)
                }
                Steal::Retry => continue,
            }
        }
    }

    None
}

fn try_terminate(nworkers_stage: &AtomicUsize) -> bool {
    enter_stage(nworkers_stage);
    thread::sleep_ms(1);
    !try_exit(nworkers_stage)
}

fn enter_stage(atomic: &AtomicUsize) -> bool {
    atomic.fetch_sub(1, Ordering::SeqCst) == 1
}

fn try_exit(atomic: &AtomicUsize) -> bool {
    let mut nworkers = atomic.load(Ordering::Relaxed);

    loop {
        if nworkers == 0 {
            return false;
        }
        let prev_nworkers = atomic.compare_and_swap(nworkers, nworkers + 1, Ordering::SeqCst);

        if nworkers == prev_nworkers {
            return true;
        }

        nworkers = prev_nworkers;
    }
}

const SEGMENT_SIZE: usize = 4;

struct Segment {
    data: Vec<Address>,
}

impl Segment {
    fn new() -> Segment {
        Segment {
            data: Vec::with_capacity(SEGMENT_SIZE),
        }
    }

    fn empty() -> Segment {
        Segment { data: Vec::new() }
    }

    fn with(addr: Address) -> Segment {
        let mut segment = Segment::new();
        segment.data.push(addr);

        segment
    }

    fn has_capacity(&self) -> bool {
        self.data.len() < SEGMENT_SIZE
    }

    fn is_empty(&self) -> bool {
        self.data.is_empty()
    }

    fn push(&mut self, addr: Address) {
        debug_assert!(self.has_capacity());
        self.data.push(addr);
    }

    fn pop(&mut self) -> Option<Address> {
        self.data.pop()
    }
}
<commit_msg>push single addresses instead of segments<commit_after>use std::mem;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;

use crossbeam_deque::{self as deque, Pop, Steal, Stealer, Worker};
use threadpool::ThreadPool;

use gc::root::Slot;
use gc::{Address, Region};

pub fn start(rootset: &[Slot], heap: Region, perm: Region, number_workers: usize) {
    let mut workers = Vec::with_capacity(number_workers);
    let mut stealers = Vec::with_capacity(number_workers);

    for _ in 0..number_workers {
        let (w, s) = deque::lifo();
        workers.push(w);
        stealers.push(s);
    }

    for root in rootset {
        let root_ptr = root.get();

        if heap.contains(root_ptr) {
            let root_obj = root_ptr.to_mut_obj();

            if !root_obj.header().is_marked_non_atomic() {
                root_obj.header_mut().mark_non_atomic();
                workers[0].push(root_ptr);
            }
        } else {
            debug_assert!(root_ptr.is_null() || perm.contains(root_ptr));
        }
    }

    let pool = ThreadPool::with_name("gc-worker".to_string(), number_workers);
    let nworkers_stage = Arc::new(AtomicUsize::new(number_workers));

    for (task_id, worker) in workers.into_iter().enumerate() {
        let heap_region = heap.clone();
        let perm_region = perm.clone();

        let stealers = stealers.clone();
        let nworkers_stage = nworkers_stage.clone();

        pool.execute(move || {
            let mut local_segment = Segment::new();

            loop {
                let object_addr = if !local_segment.is_empty() {
                    local_segment.pop().expect("should be non-empty")
                } else {
                    if let Some(address) = pop(task_id, &worker, &stealers) {
                        address
                    } else if try_terminate(&nworkers_stage) {
                        break;
                    } else {
                        continue;
                    }
                };

                let object = object_addr.to_mut_obj();

                object.visit_reference_fields(|field| {
                    let field_addr = field.get();

                    if heap_region.contains(field_addr) {
                        let field_obj = field_addr.to_mut_obj();

                        if field_obj.header().try_mark() {
                            if local_segment.has_capacity() {
                                local_segment.push(field_addr);
                            } else {
                                // let new_local_segment = Segment::with(field_addr);
                                // let old_local_segment =
                                    // mem::replace(&mut local_segment, new_local_segment);
                                worker.push(field_addr);
                            }
                        }
                    } else {
                        debug_assert!(field_addr.is_null() || perm_region.contains(field_addr));
                    }
                });
            }
        });
    }

    pool.join();
}

fn pop(task_id: usize, worker: &Worker<Address>, stealers: &[Stealer<Address>]) -> Option<Address> {
    loop {
        match worker.pop() {
            Pop::Empty => break,
            Pop::Data(address) => {
                return Some(address)
            }
            Pop::Retry => continue,
        }
    }

    for stealer in stealers {
        loop {
            match stealer.steal() {
                Steal::Empty => break,
                Steal::Data(address) => {
                    return Some(address)
                }
                Steal::Retry => continue,
            }
        }
    }

    None
}

fn try_terminate(nworkers_stage: &AtomicUsize) -> bool {
    enter_stage(nworkers_stage);
    thread::sleep_ms(1);
    !try_exit(nworkers_stage)
}

fn enter_stage(atomic: &AtomicUsize) -> bool {
    atomic.fetch_sub(1, Ordering::SeqCst) == 1
}

fn try_exit(atomic: &AtomicUsize) -> bool {
    let mut nworkers = atomic.load(Ordering::Relaxed);

    loop {
        if nworkers == 0 {
            return false;
        }
        let prev_nworkers = atomic.compare_and_swap(nworkers, nworkers + 1, Ordering::SeqCst);

        if nworkers == prev_nworkers {
            return true;
        }

        nworkers = prev_nworkers;
    }
}

const SEGMENT_SIZE: usize = 8;

struct Segment {
    data: Vec<Address>,
}

impl Segment {
    fn new() -> Segment {
        Segment {
            data: Vec::with_capacity(SEGMENT_SIZE),
        }
    }

    fn empty() -> Segment {
        Segment { data: Vec::new() }
    }

    fn with(addr: Address) -> Segment {
        let mut segment = Segment::new();
        segment.data.push(addr);

        segment
    }

    fn has_capacity(&self) -> bool {
        self.data.len() < SEGMENT_SIZE
    }

    fn is_empty(&self) -> bool {
        self.data.is_empty()
    }

    fn push(&mut self, addr: Address) {
        debug_assert!(self.has_capacity());
        self.data.push(addr);
    }

    fn pop(&mut self) -> Option<Address> {
        self.data.pop()
    }
}
<|endoftext|>


<commit_before>fn main() {
  println!("Hello, world!");
}<commit_msg>(feat) Default mime generation.<commit_after>use std::io::IoResult;

use super::{get_reader, get_writer};

pub fn generate(list: Path, module: Path) -> IoResult<()> {
    let mut reader = get_reader(list);
    let mut writer = get_writer(module);

    try!(writer.write(b"\
// This automatically generated file is included in response.rs.
use std::path::BytesContainer;

use http::headers::content_type::MediaType;

pub fn get_content_type(path: &Path) -> Option<MediaType> {
    let path_str = path.container_as_str().unwrap();
    let ext_pos = regex!(\".[a-z0-9]+$\").find(path_str);
    let mut ext;
    match ext_pos {
        Some((start, _)) => {
            ext = path_str.as_slice().slice_from(start);
        },
        None => return None
    }

    match ext {
"));

    /* Generated snippets will look like:
    "json" => Some(MediaType {
        type_: "application".to_str(),
        subtype: "json".to_str(),
        parameters: vec![]
    }),
    */

    // loop over lines
        // loop over fields
            // populate the enum

writer.write(b"        _ => None
    }
}
")
}
<|endoftext|>


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

//! This pass adds validation calls (AcquireValid, ReleaseValid) where appropriate.
//! It has to be run really early, before transformations like inlining, because
//! introducing these calls *adds* UB -- so, conceptually, this pass is actually part
//! of MIR building, and only after this pass we think of the program has having the
//! normal MIR semantics.

use rustc::ty::{self, TyCtxt, RegionKind};
use rustc::hir;
use rustc::mir::*;
use rustc::mir::transform::{MirPass, MirSource};
use rustc::middle::region::CodeExtent;

pub struct AddValidation;

/// Determine the "context" of the lval: Mutability and region.
fn lval_context<'a, 'tcx, D>(
    lval: &Lvalue<'tcx>,
    local_decls: &D,
    tcx: TyCtxt<'a, 'tcx, 'tcx>
) -> (Option<CodeExtent>, hir::Mutability)
    where D: HasLocalDecls<'tcx>
{
    use rustc::mir::Lvalue::*;

    match *lval {
        Local { .. } => (None, hir::MutMutable),
        Static(_) => (None, hir::MutImmutable),
        Projection(ref proj) => {
            match proj.elem {
                ProjectionElem::Deref => {
                    // Computing the inside the recursion makes this quadratic.
                    // We don't expect deep paths though.
                    let ty = proj.base.ty(local_decls, tcx).to_ty(tcx);
                    // A Deref projection may restrict the context, this depends on the type
                    // being deref'd.
                    let context = match ty.sty {
                        ty::TyRef(re, tam) => {
                            let re = match re {
                                &RegionKind::ReScope(ce) => Some(ce),
                                &RegionKind::ReErased =>
                                    bug!("AddValidation pass must be run before erasing lifetimes"),
                                _ => None
                            };
                            (re, tam.mutbl)
                        }
                        ty::TyRawPtr(_) =>
                            // There is no guarantee behind even a mutable raw pointer,
                            // no write locks are acquired there, so we also don't want to
                            // release any.
                            (None, hir::MutImmutable),
                        ty::TyAdt(adt, _) if adt.is_box() => (None, hir::MutMutable),
                        _ => bug!("Deref on a non-pointer type {:?}", ty),
                    };
                    // "Intersect" this restriction with proj.base.
                    if let (Some(_), hir::MutImmutable) = context {
                        // This is already as restricted as it gets, no need to even recurse
                        context
                    } else {
                        let base_context = lval_context(&proj.base, local_decls, tcx);
                        // The region of the outermost Deref is always most restrictive.
                        let re = context.0.or(base_context.0);
                        let mutbl = context.1.and(base_context.1);
                        (re, mutbl)
                    }

                }
                _ => lval_context(&proj.base, local_decls, tcx),
            }
        }
    }
}

impl MirPass for AddValidation {
    fn run_pass<'a, 'tcx>(&self,
                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
                          _: MirSource,
                          mir: &mut Mir<'tcx>) {
        if !tcx.sess.opts.debugging_opts.mir_emit_validate {
            return;
        }

        let local_decls = mir.local_decls.clone(); // FIXME: Find a way to get rid of this clone.

        /// Convert an lvalue to a validation operand.
        let lval_to_operand = |lval: Lvalue<'tcx>| -> ValidationOperand<'tcx, Lvalue<'tcx>> {
            let (re, mutbl) = lval_context(&lval, &local_decls, tcx);
            let ty = lval.ty(&local_decls, tcx).to_ty(tcx);
            ValidationOperand { lval, ty, re, mutbl }
        };

        // PART 1
        // Add an AcquireValid at the beginning of the start block.
        if mir.arg_count > 0 {
            let acquire_stmt = Statement {
                source_info: SourceInfo {
                    scope: ARGUMENT_VISIBILITY_SCOPE,
                    span: mir.span, // FIXME: Consider using just the span covering the function
                                    // argument declaration.
                },
                kind: StatementKind::Validate(ValidationOp::Acquire,
                    // Skip return value, go over all the arguments
                    mir.local_decls.iter_enumerated().skip(1).take(mir.arg_count)
                    .map(|(local, _)| lval_to_operand(Lvalue::Local(local))).collect()
                )
            };
            mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, acquire_stmt);
        }

        // PART 2
        // Add ReleaseValid/AcquireValid around function call terminators.  We don't use a visitor
        // because we need to access the block that a Call jumps to.
        let mut returns : Vec<(SourceInfo, Lvalue<'tcx>, BasicBlock)> = Vec::new();
        for block_data in mir.basic_blocks_mut() {
            match block_data.terminator {
                Some(Terminator { kind: TerminatorKind::Call { ref args, ref destination, .. },
                                  source_info }) => {
                    // Before the call: Release all arguments
                    let release_stmt = Statement {
                        source_info,
                        kind: StatementKind::Validate(ValidationOp::Release,
                            args.iter().filter_map(|op| {
                                match op {
                                    &Operand::Consume(ref lval) =>
                                        Some(lval_to_operand(lval.clone())),
                                    &Operand::Constant(..) => { None },
                                }
                            }).collect())
                    };
                    block_data.statements.push(release_stmt);
                    // Remember the return destination for later
                    if let &Some(ref destination) = destination {
                        returns.push((source_info, destination.0.clone(), destination.1));
                    }
                }
                Some(Terminator { kind: TerminatorKind::Drop { location: ref lval, .. },
                                  source_info }) |
                Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref lval, .. },
                                  source_info }) => {
                    // Before the call: Release all arguments
                    let release_stmt = Statement {
                        source_info,
                        kind: StatementKind::Validate(ValidationOp::Release,
                                vec![lval_to_operand(lval.clone())]),
                    };
                    block_data.statements.push(release_stmt);
                    // drop doesn't return anything, so we need no acquire.
                }
                _ => {
                    // Not a block ending in a Call -> ignore.
                }
            }
        }
        // Now we go over the returns we collected to acquire the return values.
        for (source_info, dest_lval, dest_block) in returns {
            let acquire_stmt = Statement {
                source_info,
                kind: StatementKind::Validate(ValidationOp::Acquire,
                        vec![lval_to_operand(dest_lval)]),
            };
            mir.basic_blocks_mut()[dest_block].statements.insert(0, acquire_stmt);
        }

        // PART 3
        // Add ReleaseValid/AcquireValid around Ref.  Again an iterator does not seem very suited
        // as we need to add new statements before and after each Ref.
        for block_data in mir.basic_blocks_mut() {
            // We want to insert statements around Ref commands as we iterate.  To this end, we
            // iterate backwards using indices.
            for i in (0..block_data.statements.len()).rev() {
                let (dest_lval, re, src_lval) = match block_data.statements[i].kind {
                    StatementKind::Assign(ref dest_lval, Rvalue::Ref(re, _, ref src_lval)) => {
                        (dest_lval.clone(), re, src_lval.clone())
                    },
                    _ => continue,
                };
                // So this is a ref, and we got all the data we wanted.
                // Do an acquire of the result -- but only what it points to, so add a Deref
                // projection.
                let dest_lval = Projection { base: dest_lval, elem: ProjectionElem::Deref };
                let dest_lval = Lvalue::Projection(Box::new(dest_lval));
                let acquire_stmt = Statement {
                    source_info: block_data.statements[i].source_info,
                    kind: StatementKind::Validate(ValidationOp::Acquire,
                            vec![lval_to_operand(dest_lval)]),
                };
                block_data.statements.insert(i+1, acquire_stmt);

                // The source is released until the region of the borrow ends.
                let op = match re {
                    &RegionKind::ReScope(ce) => ValidationOp::Suspend(ce),
                    &RegionKind::ReErased =>
                        bug!("AddValidation pass must be run before erasing lifetimes"),
                    _ => ValidationOp::Release,
                };
                let release_stmt = Statement {
                    source_info: block_data.statements[i].source_info,
                    kind: StatementKind::Validate(op, vec![lval_to_operand(src_lval)]),
                };
                block_data.statements.insert(i, release_stmt);
            }
        }
    }
}
<commit_msg>also release/validate around non-Misc casts<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This pass adds validation calls (AcquireValid, ReleaseValid) where appropriate.
//! It has to be run really early, before transformations like inlining, because
//! introducing these calls *adds* UB -- so, conceptually, this pass is actually part
//! of MIR building, and only after this pass we think of the program has having the
//! normal MIR semantics.

use rustc::ty::{self, TyCtxt, RegionKind};
use rustc::hir;
use rustc::mir::*;
use rustc::mir::transform::{MirPass, MirSource};
use rustc::middle::region::CodeExtent;

pub struct AddValidation;

/// Determine the "context" of the lval: Mutability and region.
fn lval_context<'a, 'tcx, D>(
    lval: &Lvalue<'tcx>,
    local_decls: &D,
    tcx: TyCtxt<'a, 'tcx, 'tcx>
) -> (Option<CodeExtent>, hir::Mutability)
    where D: HasLocalDecls<'tcx>
{
    use rustc::mir::Lvalue::*;

    match *lval {
        Local { .. } => (None, hir::MutMutable),
        Static(_) => (None, hir::MutImmutable),
        Projection(ref proj) => {
            match proj.elem {
                ProjectionElem::Deref => {
                    // Computing the inside the recursion makes this quadratic.
                    // We don't expect deep paths though.
                    let ty = proj.base.ty(local_decls, tcx).to_ty(tcx);
                    // A Deref projection may restrict the context, this depends on the type
                    // being deref'd.
                    let context = match ty.sty {
                        ty::TyRef(re, tam) => {
                            let re = match re {
                                &RegionKind::ReScope(ce) => Some(ce),
                                &RegionKind::ReErased =>
                                    bug!("AddValidation pass must be run before erasing lifetimes"),
                                _ => None
                            };
                            (re, tam.mutbl)
                        }
                        ty::TyRawPtr(_) =>
                            // There is no guarantee behind even a mutable raw pointer,
                            // no write locks are acquired there, so we also don't want to
                            // release any.
                            (None, hir::MutImmutable),
                        ty::TyAdt(adt, _) if adt.is_box() => (None, hir::MutMutable),
                        _ => bug!("Deref on a non-pointer type {:?}", ty),
                    };
                    // "Intersect" this restriction with proj.base.
                    if let (Some(_), hir::MutImmutable) = context {
                        // This is already as restricted as it gets, no need to even recurse
                        context
                    } else {
                        let base_context = lval_context(&proj.base, local_decls, tcx);
                        // The region of the outermost Deref is always most restrictive.
                        let re = context.0.or(base_context.0);
                        let mutbl = context.1.and(base_context.1);
                        (re, mutbl)
                    }

                }
                _ => lval_context(&proj.base, local_decls, tcx),
            }
        }
    }
}

impl MirPass for AddValidation {
    fn run_pass<'a, 'tcx>(&self,
                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
                          _: MirSource,
                          mir: &mut Mir<'tcx>) {
        if !tcx.sess.opts.debugging_opts.mir_emit_validate {
            return;
        }

        let local_decls = mir.local_decls.clone(); // FIXME: Find a way to get rid of this clone.

        /// Convert an lvalue to a validation operand.
        let lval_to_operand = |lval: Lvalue<'tcx>| -> ValidationOperand<'tcx, Lvalue<'tcx>> {
            let (re, mutbl) = lval_context(&lval, &local_decls, tcx);
            let ty = lval.ty(&local_decls, tcx).to_ty(tcx);
            ValidationOperand { lval, ty, re, mutbl }
        };

        // PART 1
        // Add an AcquireValid at the beginning of the start block.
        if mir.arg_count > 0 {
            let acquire_stmt = Statement {
                source_info: SourceInfo {
                    scope: ARGUMENT_VISIBILITY_SCOPE,
                    span: mir.span, // FIXME: Consider using just the span covering the function
                                    // argument declaration.
                },
                kind: StatementKind::Validate(ValidationOp::Acquire,
                    // Skip return value, go over all the arguments
                    mir.local_decls.iter_enumerated().skip(1).take(mir.arg_count)
                    .map(|(local, _)| lval_to_operand(Lvalue::Local(local))).collect()
                )
            };
            mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, acquire_stmt);
        }

        // PART 2
        // Add ReleaseValid/AcquireValid around function call terminators.  We don't use a visitor
        // because we need to access the block that a Call jumps to.
        let mut returns : Vec<(SourceInfo, Lvalue<'tcx>, BasicBlock)> = Vec::new();
        for block_data in mir.basic_blocks_mut() {
            match block_data.terminator {
                Some(Terminator { kind: TerminatorKind::Call { ref args, ref destination, .. },
                                  source_info }) => {
                    // Before the call: Release all arguments
                    let release_stmt = Statement {
                        source_info,
                        kind: StatementKind::Validate(ValidationOp::Release,
                            args.iter().filter_map(|op| {
                                match op {
                                    &Operand::Consume(ref lval) =>
                                        Some(lval_to_operand(lval.clone())),
                                    &Operand::Constant(..) => { None },
                                }
                            }).collect())
                    };
                    block_data.statements.push(release_stmt);
                    // Remember the return destination for later
                    if let &Some(ref destination) = destination {
                        returns.push((source_info, destination.0.clone(), destination.1));
                    }
                }
                Some(Terminator { kind: TerminatorKind::Drop { location: ref lval, .. },
                                  source_info }) |
                Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref lval, .. },
                                  source_info }) => {
                    // Before the call: Release all arguments
                    let release_stmt = Statement {
                        source_info,
                        kind: StatementKind::Validate(ValidationOp::Release,
                                vec![lval_to_operand(lval.clone())]),
                    };
                    block_data.statements.push(release_stmt);
                    // drop doesn't return anything, so we need no acquire.
                }
                _ => {
                    // Not a block ending in a Call -> ignore.
                }
            }
        }
        // Now we go over the returns we collected to acquire the return values.
        for (source_info, dest_lval, dest_block) in returns {
            let acquire_stmt = Statement {
                source_info,
                kind: StatementKind::Validate(ValidationOp::Acquire,
                        vec![lval_to_operand(dest_lval)]),
            };
            mir.basic_blocks_mut()[dest_block].statements.insert(0, acquire_stmt);
        }

        // PART 3
        // Add ReleaseValid/AcquireValid around Ref and Cast.  Again an iterator does not seem very
        // suited
        // as we need to add new statements before and after each Ref.
        for block_data in mir.basic_blocks_mut() {
            // We want to insert statements around Ref commands as we iterate.  To this end, we
            // iterate backwards using indices.
            for i in (0..block_data.statements.len()).rev() {
                match block_data.statements[i].kind {
                    // When the borrow of this ref expires, we need to recover validation.
                    StatementKind::Assign(_, Rvalue::Ref(_, _, _)) => {
                        // Due to a lack of NLL; we can't capture anything directly here.
                        // Instead, we have to re-match and clone there.
                        let (dest_lval, re, src_lval) = match block_data.statements[i].kind {
                            StatementKind::Assign(ref dest_lval,
                                                  Rvalue::Ref(re, _, ref src_lval)) => {
                                (dest_lval.clone(), re, src_lval.clone())
                            },
                            _ => bug!("We already matched this."),
                        };
                        // So this is a ref, and we got all the data we wanted.
                        // Do an acquire of the result -- but only what it points to, so add a Deref
                        // projection.
                        let dest_lval = Projection { base: dest_lval, elem: ProjectionElem::Deref };
                        let dest_lval = Lvalue::Projection(Box::new(dest_lval));
                        let acquire_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(ValidationOp::Acquire,
                                    vec![lval_to_operand(dest_lval)]),
                        };
                        block_data.statements.insert(i+1, acquire_stmt);

                        // The source is released until the region of the borrow ends.
                        let op = match re {
                            &RegionKind::ReScope(ce) => ValidationOp::Suspend(ce),
                            &RegionKind::ReErased =>
                                bug!("AddValidation pass must be run before erasing lifetimes"),
                            _ => ValidationOp::Release,
                        };
                        let release_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(op, vec![lval_to_operand(src_lval)]),
                        };
                        block_data.statements.insert(i, release_stmt);
                    }
                    // Casts can change what validation does (e.g. unsizing)
                    StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Consume(_), _))
                        if kind != CastKind::Misc =>
                    {
                        // Due to a lack of NLL; we can't capture anything directly here.
                        // Instead, we have to re-match and clone there.
                        let (dest_lval, src_lval) = match block_data.statements[i].kind {
                            StatementKind::Assign(ref dest_lval,
                                    Rvalue::Cast(_, Operand::Consume(ref src_lval), _)) =>
                            {
                                (dest_lval.clone(), src_lval.clone())
                            },
                            _ => bug!("We already matched this."),
                        };

                        // Acquire of the result
                        let acquire_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(ValidationOp::Acquire,
                                    vec![lval_to_operand(dest_lval)]),
                        };
                        block_data.statements.insert(i+1, acquire_stmt);

                        // Release of the input
                        let release_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(ValidationOp::Release,
                                                            vec![lval_to_operand(src_lval)]),
                        };
                        block_data.statements.insert(i, release_stmt);
                    }
                    _ => {},
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;

use super::AudioFilter;
use super::{MappedCtrl, VolumeCtrl};
use super::{Mixer, MixerConfig};

#[derive(Clone)]
pub struct SoftMixer {
    // There is no AtomicF64, so we store the f64 as bits in a u64 field.
    // It's much faster than a Mutex<f64>.
    volume: Arc<AtomicU64>,
    volume_ctrl: VolumeCtrl,
}

impl Mixer for SoftMixer {
    fn open(config: MixerConfig) -> Self {
        let volume_ctrl = config.volume_ctrl;
        info!("Mixing with softvol and volume control: {:?}", volume_ctrl);

        Self {
            volume: Arc::new(AtomicU64::new(f64::to_bits(0.5))),
            volume_ctrl,
        }
    }

    fn volume(&self) -> u16 {
        let mapped_volume = f64::from_bits(self.volume.load(Ordering::Relaxed));
        self.volume_ctrl.from_mapped(mapped_volume)
    }

    fn set_volume(&self, volume: u16) {
        let mapped_volume = self.volume_ctrl.to_mapped(volume);
        self.volume
            .store(mapped_volume.to_bits(), Ordering::Relaxed)
    }

    fn get_audio_filter(&self) -> Option<Box<dyn AudioFilter + Send>> {
        Some(Box::new(SoftVolumeApplier {
            volume: self.volume.clone(),
        }))
    }
}

impl SoftMixer {
    pub const NAME: &'static str = "softmixer";
}

struct SoftVolumeApplier {
    volume: Arc<AtomicU64>,
}

impl AudioFilter for SoftVolumeApplier {
    fn modify_stream(&self, data: &mut [f64]) {
        let volume = f64::from_bits(self.volume.load(Ordering::Relaxed));
        if volume < 1.0 {
            for x in data.iter_mut() {
                *x *= volume;
            }
        }
    }
}
<commit_msg>Revert name to softvol<commit_after>use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;

use super::AudioFilter;
use super::{MappedCtrl, VolumeCtrl};
use super::{Mixer, MixerConfig};

#[derive(Clone)]
pub struct SoftMixer {
    // There is no AtomicF64, so we store the f64 as bits in a u64 field.
    // It's much faster than a Mutex<f64>.
    volume: Arc<AtomicU64>,
    volume_ctrl: VolumeCtrl,
}

impl Mixer for SoftMixer {
    fn open(config: MixerConfig) -> Self {
        let volume_ctrl = config.volume_ctrl;
        info!("Mixing with softvol and volume control: {:?}", volume_ctrl);

        Self {
            volume: Arc::new(AtomicU64::new(f64::to_bits(0.5))),
            volume_ctrl,
        }
    }

    fn volume(&self) -> u16 {
        let mapped_volume = f64::from_bits(self.volume.load(Ordering::Relaxed));
        self.volume_ctrl.from_mapped(mapped_volume)
    }

    fn set_volume(&self, volume: u16) {
        let mapped_volume = self.volume_ctrl.to_mapped(volume);
        self.volume
            .store(mapped_volume.to_bits(), Ordering::Relaxed)
    }

    fn get_audio_filter(&self) -> Option<Box<dyn AudioFilter + Send>> {
        Some(Box::new(SoftVolumeApplier {
            volume: self.volume.clone(),
        }))
    }
}

impl SoftMixer {
    pub const NAME: &'static str = "softvol";
}

struct SoftVolumeApplier {
    volume: Arc<AtomicU64>,
}

impl AudioFilter for SoftVolumeApplier {
    fn modify_stream(&self, data: &mut [f64]) {
        let volume = f64::from_bits(self.volume.load(Ordering::Relaxed));
        if volume < 1.0 {
            for x in data.iter_mut() {
                *x *= volume;
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use crate::botocore::{Member, Operation, ServiceDefinition, Shape, ShapeType, Value};
use crate::cargo;
use crate::config::ServiceConfig;

#[derive(Debug)]
pub struct Service<'a> {
    config: &'a crate::ServiceConfig,
    definition: ServiceDefinition,
}

impl<'b> Service<'b> {
    pub fn new(config: &'b ServiceConfig, definition: ServiceDefinition) -> Self {
        Service { config, definition }
    }

    pub fn name(&self) -> &str {
        match self.definition.metadata.service_abbreviation {
            Some(ref service_abbreviation) => service_abbreviation.as_str(),
            None => self.definition.metadata.service_full_name.as_ref(),
        }
    }

    pub fn full_name(&self) -> &str {
        &self.definition.metadata.service_full_name
    }

    pub fn service_id(&self) -> Option<&str> {
        self.definition
            .metadata
            .service_id
            .as_ref()
            .map(std::string::String::as_str)
    }

    pub fn documentation(&self) -> Option<&String> {
        self.definition.documentation.as_ref()
    }

    pub fn protocol(&self) -> &str {
        &self.definition.metadata.protocol
    }

    pub fn client_type_name(&self) -> String {
        format!("{}Client", self.service_type_name())
    }

    pub fn service_type_name(&self) -> &str {
        &self.config.base_type_name
    }

    pub fn endpoint_prefix(&self) -> &str {
        &self.definition.metadata.endpoint_prefix
    }

    pub fn api_version(&self) -> &str {
        &self.definition.metadata.api_version
    }

    pub fn target_prefix(&self) -> Option<&String> {
        self.definition.metadata.target_prefix.as_ref()
    }

    pub fn json_version(&self) -> Option<&String> {
        self.definition.metadata.json_version.as_ref()
    }

    pub fn shapes(&self) -> &BTreeMap<String, Shape> {
        &self.definition.shapes
    }

    pub fn get_shape(&self, name: &str) -> Option<&Shape> {
        self.definition.shapes.get(name)
    }

    pub fn operations(&self) -> &BTreeMap<String, Operation> {
        &self.definition.operations
    }

    pub fn shape_for_value<'a>(&'a self, value: &Value) -> Option<&'a Shape> {
        self.definition.shapes.get(&value.shape)
    }

    pub fn shape_for_member<'a>(&'a self, member: &Member) -> Option<&'a Shape> {
        self.definition.shapes.get(&member.shape)
    }

    pub fn shape_type_for_member<'a>(&'a self, member: &Member) -> Option<ShapeType> {
        self.definition
            .shapes
            .get(&member.shape)
            .map(|shape| shape.shape_type)
    }

    pub fn signing_name(&self) -> String {
        match self.definition.metadata.signing_name {
            Some(ref signing_name) => signing_name.to_string(),
            None => self.definition.metadata.endpoint_prefix.to_string(),
        }
    }

    pub fn needs_serde_json_crate(&self) -> bool {
        match self.name() {
            "AmazonApiGatewayManagementApi"
            | "Amazon CloudSearch Domain"
            | "AWS Mobile"
            | "AWS IoT Data Plane"
            | "Amazon SageMaker Runtime"
            | "Amazon WorkMail Message Flow"
            | "SSO"
            | "Amazon EBS" => false,
            _ => true,
        }
    }

    pub fn get_dependencies(&self) -> BTreeMap<String, cargo::Dependency> {
        let mut dependencies = BTreeMap::new();

        dependencies.insert(
            "async-trait".to_owned(),
            cargo::Dependency::Simple("0.1".into()),
        );
        dependencies.insert("bytes".to_owned(), cargo::Dependency::Simple("0.5".into()));
        dependencies.insert(
            "futures".to_owned(),
            cargo::Dependency::Extended {
                path: None,
                version: Some("0.3".to_owned()),
                optional: None,
                default_features: None,
                features: None,
            },
        );
        dependencies.insert(
            "rusoto_core".to_owned(),
            cargo::Dependency::Extended {
                path: Some("../../core".into()),
                version: Some(self.config.core_version.clone()),
                optional: None,
                default_features: Some(false),
                features: None,
            },
        );

        match self.protocol() {
            "json" => {
                dependencies.insert(
                    "serde".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0".into()),
                        path: None,
                        optional: None,
                        default_features: None,
                        features: Some(vec!["derive".into()]),
                    },
                );
                dependencies.insert(
                    "serde_json".to_owned(),
                    cargo::Dependency::Simple("1.0".into()),
                );
            }
            "query" | "ec2" => {
                dependencies.insert(
                    "serde".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0.2".into()),
                        optional: Some(true),
                        path: None,
                        default_features: None,
                        features: None,
                    },
                );
                dependencies.insert(
                    "serde_derive".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0.2".into()),
                        optional: Some(true),
                        path: None,
                        default_features: None,
                        features: None,
                    },
                );
                dependencies.insert(
                    "serde_urlencoded".to_owned(),
                    cargo::Dependency::Simple("0.6".into()),
                );
                dependencies.insert("xml-rs".to_owned(), cargo::Dependency::Simple("0.8".into()));
            }
            "rest-xml" => {
                dependencies.insert(
                    "serde".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0.2".into()),
                        optional: Some(true),
                        path: None,
                        default_features: None,
                        features: None,
                    },
                );
                dependencies.insert(
                    "serde_derive".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0.2".into()),
                        optional: Some(true),
                        path: None,
                        default_features: None,
                        features: None,
                    },
                );
                dependencies.insert("xml-rs".to_owned(), cargo::Dependency::Simple("0.8".into()));
            }
            "rest-json" => {
                dependencies.insert(
                    "serde".to_owned(),
                    cargo::Dependency::Simple("1.0.2".into()),
                );
                dependencies.insert(
                    "serde_derive".to_owned(),
                    cargo::Dependency::Simple("1.0.2".into()),
                );
                // some rest-json services don't use the `serde_json` crate:
                if self.needs_serde_json_crate() {
                    dependencies.insert(
                        "serde_json".to_owned(),
                        cargo::Dependency::Simple("1.0".into()),
                    );
                }
            }
            protocol => panic!("Unknown protocol {}", protocol),
        }

        if let Some(ref custom_dependencies) = self.config.custom_dependencies {
            dependencies.extend(custom_dependencies.clone());
        }

        dependencies
    }

    pub fn get_dev_dependencies(&self) -> BTreeMap<String, cargo::Dependency> {
        let mut dev_dependencies = BTreeMap::new();

        dev_dependencies.insert(
            "rusoto_mock".to_owned(),
            cargo::Dependency::Extended {
                path: Some("../../../mock".into()),
                version: Some(self.config.core_version.clone()),
                optional: None,
                default_features: Some(false),
                features: None,
            },
        );

        dev_dependencies.insert(
            "tokio".to_owned(),
            cargo::Dependency::Simple("0.2".to_owned()),
        );

        if let Some(ref custom_dev_dependencies) = self.config.custom_dev_dependencies {
            dev_dependencies.extend(custom_dev_dependencies.clone());
        }

        dev_dependencies
    }

    pub fn visit_shapes<F>(&self, shape_name: &str, visitor: &mut F)
    where
        F: FnMut(&str, &Shape) -> bool,
    {
        let shape = self
            .get_shape(shape_name)
            .expect("Shape missing from service definition");

        if !visitor(shape_name, shape) {
            return;
        }

        match shape.shape_type {
            ShapeType::Structure => {
                if let Some(ref members) = shape.members {
                    for member in members.values() {
                        self.visit_shapes(&member.shape, visitor);
                    }
                }
            }
            ShapeType::Map => {
                self.visit_shapes(shape.key_type(), visitor);
                self.visit_shapes(shape.value_type(), visitor);
            }
            ShapeType::List => {
                self.visit_shapes(shape.member_type(), visitor);
            }
            _ => {}
        }
    }
}
<commit_msg>Add serde_json for EBS<commit_after>use std::collections::BTreeMap;

use crate::botocore::{Member, Operation, ServiceDefinition, Shape, ShapeType, Value};
use crate::cargo;
use crate::config::ServiceConfig;

#[derive(Debug)]
pub struct Service<'a> {
    config: &'a crate::ServiceConfig,
    definition: ServiceDefinition,
}

impl<'b> Service<'b> {
    pub fn new(config: &'b ServiceConfig, definition: ServiceDefinition) -> Self {
        Service { config, definition }
    }

    pub fn name(&self) -> &str {
        match self.definition.metadata.service_abbreviation {
            Some(ref service_abbreviation) => service_abbreviation.as_str(),
            None => self.definition.metadata.service_full_name.as_ref(),
        }
    }

    pub fn full_name(&self) -> &str {
        &self.definition.metadata.service_full_name
    }

    pub fn service_id(&self) -> Option<&str> {
        self.definition
            .metadata
            .service_id
            .as_ref()
            .map(std::string::String::as_str)
    }

    pub fn documentation(&self) -> Option<&String> {
        self.definition.documentation.as_ref()
    }

    pub fn protocol(&self) -> &str {
        &self.definition.metadata.protocol
    }

    pub fn client_type_name(&self) -> String {
        format!("{}Client", self.service_type_name())
    }

    pub fn service_type_name(&self) -> &str {
        &self.config.base_type_name
    }

    pub fn endpoint_prefix(&self) -> &str {
        &self.definition.metadata.endpoint_prefix
    }

    pub fn api_version(&self) -> &str {
        &self.definition.metadata.api_version
    }

    pub fn target_prefix(&self) -> Option<&String> {
        self.definition.metadata.target_prefix.as_ref()
    }

    pub fn json_version(&self) -> Option<&String> {
        self.definition.metadata.json_version.as_ref()
    }

    pub fn shapes(&self) -> &BTreeMap<String, Shape> {
        &self.definition.shapes
    }

    pub fn get_shape(&self, name: &str) -> Option<&Shape> {
        self.definition.shapes.get(name)
    }

    pub fn operations(&self) -> &BTreeMap<String, Operation> {
        &self.definition.operations
    }

    pub fn shape_for_value<'a>(&'a self, value: &Value) -> Option<&'a Shape> {
        self.definition.shapes.get(&value.shape)
    }

    pub fn shape_for_member<'a>(&'a self, member: &Member) -> Option<&'a Shape> {
        self.definition.shapes.get(&member.shape)
    }

    pub fn shape_type_for_member<'a>(&'a self, member: &Member) -> Option<ShapeType> {
        self.definition
            .shapes
            .get(&member.shape)
            .map(|shape| shape.shape_type)
    }

    pub fn signing_name(&self) -> String {
        match self.definition.metadata.signing_name {
            Some(ref signing_name) => signing_name.to_string(),
            None => self.definition.metadata.endpoint_prefix.to_string(),
        }
    }

    pub fn needs_serde_json_crate(&self) -> bool {
        match self.name() {
            "AmazonApiGatewayManagementApi"
            | "Amazon CloudSearch Domain"
            | "AWS Mobile"
            | "AWS IoT Data Plane"
            | "Amazon SageMaker Runtime"
            | "Amazon WorkMail Message Flow"
            | "SSO" => false,
            _ => true,
        }
    }

    pub fn get_dependencies(&self) -> BTreeMap<String, cargo::Dependency> {
        let mut dependencies = BTreeMap::new();

        dependencies.insert(
            "async-trait".to_owned(),
            cargo::Dependency::Simple("0.1".into()),
        );
        dependencies.insert("bytes".to_owned(), cargo::Dependency::Simple("0.5".into()));
        dependencies.insert(
            "futures".to_owned(),
            cargo::Dependency::Extended {
                path: None,
                version: Some("0.3".to_owned()),
                optional: None,
                default_features: None,
                features: None,
            },
        );
        dependencies.insert(
            "rusoto_core".to_owned(),
            cargo::Dependency::Extended {
                path: Some("../../core".into()),
                version: Some(self.config.core_version.clone()),
                optional: None,
                default_features: Some(false),
                features: None,
            },
        );

        match self.protocol() {
            "json" => {
                dependencies.insert(
                    "serde".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0".into()),
                        path: None,
                        optional: None,
                        default_features: None,
                        features: Some(vec!["derive".into()]),
                    },
                );
                dependencies.insert(
                    "serde_json".to_owned(),
                    cargo::Dependency::Simple("1.0".into()),
                );
            }
            "query" | "ec2" => {
                dependencies.insert(
                    "serde".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0.2".into()),
                        optional: Some(true),
                        path: None,
                        default_features: None,
                        features: None,
                    },
                );
                dependencies.insert(
                    "serde_derive".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0.2".into()),
                        optional: Some(true),
                        path: None,
                        default_features: None,
                        features: None,
                    },
                );
                dependencies.insert(
                    "serde_urlencoded".to_owned(),
                    cargo::Dependency::Simple("0.6".into()),
                );
                dependencies.insert("xml-rs".to_owned(), cargo::Dependency::Simple("0.8".into()));
            }
            "rest-xml" => {
                dependencies.insert(
                    "serde".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0.2".into()),
                        optional: Some(true),
                        path: None,
                        default_features: None,
                        features: None,
                    },
                );
                dependencies.insert(
                    "serde_derive".to_owned(),
                    cargo::Dependency::Extended {
                        version: Some("1.0.2".into()),
                        optional: Some(true),
                        path: None,
                        default_features: None,
                        features: None,
                    },
                );
                dependencies.insert("xml-rs".to_owned(), cargo::Dependency::Simple("0.8".into()));
            }
            "rest-json" => {
                dependencies.insert(
                    "serde".to_owned(),
                    cargo::Dependency::Simple("1.0.2".into()),
                );
                dependencies.insert(
                    "serde_derive".to_owned(),
                    cargo::Dependency::Simple("1.0.2".into()),
                );
                // some rest-json services don't use the `serde_json` crate:
                if self.needs_serde_json_crate() {
                    dependencies.insert(
                        "serde_json".to_owned(),
                        cargo::Dependency::Simple("1.0".into()),
                    );
                }
            }
            protocol => panic!("Unknown protocol {}", protocol),
        }

        if let Some(ref custom_dependencies) = self.config.custom_dependencies {
            dependencies.extend(custom_dependencies.clone());
        }

        dependencies
    }

    pub fn get_dev_dependencies(&self) -> BTreeMap<String, cargo::Dependency> {
        let mut dev_dependencies = BTreeMap::new();

        dev_dependencies.insert(
            "rusoto_mock".to_owned(),
            cargo::Dependency::Extended {
                path: Some("../../../mock".into()),
                version: Some(self.config.core_version.clone()),
                optional: None,
                default_features: Some(false),
                features: None,
            },
        );

        dev_dependencies.insert(
            "tokio".to_owned(),
            cargo::Dependency::Simple("0.2".to_owned()),
        );

        if let Some(ref custom_dev_dependencies) = self.config.custom_dev_dependencies {
            dev_dependencies.extend(custom_dev_dependencies.clone());
        }

        dev_dependencies
    }

    pub fn visit_shapes<F>(&self, shape_name: &str, visitor: &mut F)
    where
        F: FnMut(&str, &Shape) -> bool,
    {
        let shape = self
            .get_shape(shape_name)
            .expect("Shape missing from service definition");

        if !visitor(shape_name, shape) {
            return;
        }

        match shape.shape_type {
            ShapeType::Structure => {
                if let Some(ref members) = shape.members {
                    for member in members.values() {
                        self.visit_shapes(&member.shape, visitor);
                    }
                }
            }
            ShapeType::Map => {
                self.visit_shapes(shape.key_type(), visitor);
                self.visit_shapes(shape.value_type(), visitor);
            }
            ShapeType::List => {
                self.visit_shapes(shape.member_type(), visitor);
            }
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>use std::ops::{Deref, DerefMut};
use std::sync::mpsc::{channel, Sender, Receiver};

use types::{AudioDevice, MessageReceiver, Sample, Time};

/// Bundles a MessageReceiver with threaded message passing.
pub struct Messaged<D> where D: MessageReceiver {
    /// The device being wrapped.
    pub device: D,
    tx: Sender<D::Msg>,
    rx: Receiver<D::Msg>,
}

impl<D> Messaged<D> where D: MessageReceiver {
    /// Return the sending half of our communication channel.
    pub fn get_sender(&mut self) -> Sender<D::Msg> {
        self.tx.clone()
    }
}

impl<D> From<D> for Messaged<D> where D: MessageReceiver {
    fn from(device: D) -> Self {
        let (tx, rx) = channel();
        Messaged {
            device: device,
            tx: tx,
            rx: rx,
        }
    }
}

impl<D> AudioDevice for Messaged<D> where D: AudioDevice+MessageReceiver {
    fn num_inputs(&self) -> usize {
        self.device.num_inputs()
    }

    fn num_outputs(&self) -> usize {
        self.device.num_outputs()
    }

    fn tick(&mut self, t: Time, inputs: &[Sample], outputs: &mut[Sample]) {
        loop {
            match self.rx.try_recv() {
                Ok(msg) => self.device.handle_message(msg),
                _ => break
            }
        }
        self.device.tick(t, inputs, outputs);
    }
}

impl<D> Deref for Messaged<D> where D: MessageReceiver {
    type Target = D;
    fn deref(&self) -> &D {
        &self.device
    }
}

impl<D> DerefMut for Messaged<D> where D: MessageReceiver {
    fn deref_mut(&mut self) -> &mut D {
        &mut self.device
    }
}
<commit_msg>Document Messaged wrapper<commit_after>use std::ops::{Deref, DerefMut};
use std::sync::mpsc::{channel, Sender, Receiver};

use types::{AudioDevice, MessageReceiver, Sample, Time};

/// Bundles a `MessageReceiver` with threaded message passing.
///
/// # Sending messages
///
/// Once a device is wrapped with `Messaged`, one or more `Sender`s can be
/// returned from it. These represent the send end of standard Rust channels,
/// and can be sent between threads freely.
///
/// # As an `AudioDevice`
///
/// The wrapper implements `AudioDevice` if the contained device does as well.
/// The implementation first checks for any incoming messages, then applies them
/// to the interior device. Once the messages have been handled, it calls the
/// normal `tick` function of the device.
///
/// This allows one to wrap a device in `Messaged`, move the sender to another
/// thread, then drop the `Messaged` device into a normal container, such as
/// `DeviceChain` or `DeviceGraph`. It will automatically receive and handle its
/// own messages while processing.
///
/// # Example
///
/// ```
/// use std::thread;
/// use oxcable::chain::{DeviceChain, Tick};
/// use oxcable::oscillator::{Oscillator, Sine, SetFreq};
/// use oxcable::io::audio::AudioEngine;
/// use oxcable::wrappers::Messaged;
///
/// fn main() {
/// # // this example uses tick_forever(), so we wrap it in a function that gets
/// # // compiled by not run by doctest
/// # }
/// # fn dummy() {
///     // Initialize signal chain...
///     let tx;
///     let engine = AudioEngine::with_buffer_size(256).unwrap();
///     let mut chain = DeviceChain::from({
///         let msgd = Messaged::from(Oscillator::new(Sine).freq(440.0));
///         tx = msgd.get_sender();
///         msgd
///     }).into(
///         engine.default_output(1).unwrap()
///     );
///
///     // Send the messager to a new thread. It will wait for one second, then
///     // set the oscillator to a higher frequency.
///     thread::spawn(move || {
///         thread::sleep_ms(1000);
///         tx.send(SetFreq(880.0));
///     });
///
///     // Generate two seconds of audio, then exit...
///     chain.tick_n_times(88200);
/// }
/// ```
pub struct Messaged<D> where D: MessageReceiver {
    /// The device being wrapped.
    pub device: D,
    tx: Sender<D::Msg>,
    rx: Receiver<D::Msg>,
}

impl<D> Messaged<D> where D: MessageReceiver {
    /// Return the sending half of our communication channel.
    pub fn get_sender(&self) -> Sender<D::Msg> {
        self.tx.clone()
    }
}

impl<D> From<D> for Messaged<D> where D: MessageReceiver {
    fn from(device: D) -> Self {
        let (tx, rx) = channel();
        Messaged {
            device: device,
            tx: tx,
            rx: rx,
        }
    }
}

impl<D> AudioDevice for Messaged<D> where D: AudioDevice+MessageReceiver {
    fn num_inputs(&self) -> usize {
        self.device.num_inputs()
    }

    fn num_outputs(&self) -> usize {
        self.device.num_outputs()
    }

    fn tick(&mut self, t: Time, inputs: &[Sample], outputs: &mut[Sample]) {
        loop {
            match self.rx.try_recv() {
                Ok(msg) => self.device.handle_message(msg),
                _ => break
            }
        }
        self.device.tick(t, inputs, outputs);
    }
}

impl<D> Deref for Messaged<D> where D: MessageReceiver {
    type Target = D;
    fn deref(&self) -> &D {
        &self.device
    }
}

impl<D> DerefMut for Messaged<D> where D: MessageReceiver {
    fn deref_mut(&mut self) -> &mut D {
        &mut self.device
    }
}
<|endoftext|>