<commit_before>use direction::{Direction, Orientation};
use event::{AnyCb, Event, EventResult, Key, MouseButton, MouseEvent};
use rect::Rect;
use theme::ColorStyle;
use vec::Vec2;
use view::{Selector, View};
use xy::XY;
use Printer;
use With;

use std::cmp::min;

/// Wraps a view in a scrollable area.
pub struct ScrollView<V> {
    // The wrapped view.
    inner: V,

    // This is the size the child thinks we're giving him.
    inner_size: Vec2,

    // Offset into the inner view.
    //
    // Our `(0,0)` will be inner's `offset`
    offset: Vec2,

    // What was our own size last time we checked.
    //
    // This includes scrollbars, if any.
    last_size: Vec2,

    // Are we scrollable in each direction?
    enabled: XY<bool>,

    // Should we show scrollbars?
    //
    // Even if this is true, no scrollbar will be printed if we don't need to scroll.
    //
    // Could be an enum {Never, Auto, Always}
    show_scrollbars: bool,

    // How much padding should be between content and scrollbar?
    scrollbar_padding: Vec2,

    /// Initial position of the cursor when dragging.
    thumb_grab: Option<(Orientation, usize)>,
}

impl<V> ScrollView<V> {
    /// Creates a new ScrollView around `view`.
    pub fn new(view: V) -> Self {
        ScrollView {
            inner: view,
            inner_size: Vec2::zero(),
            offset: Vec2::zero(),
            last_size: Vec2::zero(),
            enabled: XY::new(false, true),
            show_scrollbars: true,
            scrollbar_padding: Vec2::new(1, 0),
            thumb_grab: None,
        }
    }

    /// Returns the viewport in the inner content.
    pub fn content_viewport(&self) -> Rect {
        Rect::from_size(self.offset, self.available_size())
    }

    /// Sets the scroll offset to the given value
    pub fn set_offset<S>(&mut self, offset: S)
    where
        S: Into<Vec2>,
    {
        let max_offset = self.inner_size.saturating_sub(self.available_size());
        self.offset = offset.into().or_min(max_offset);
    }

    /// Controls whether this view can scroll vertically.
    ///
    /// Defaults to `true`.
    pub fn set_scroll_y(&mut self, enabled: bool) {
        self.enabled.y = enabled;
    }

    /// Controls whether this view can scroll horizontally.
    ///
    /// Defaults to `false`.
    pub fn set_scroll_x(&mut self, enabled: bool) {
        self.enabled.x = enabled;
    }

    /// Controls whether this view can scroll vertically.
    ///
    /// Defaults to `true`.
    ///
    /// Chainable variant.
    pub fn scroll_y(self, enabled: bool) -> Self {
        self.with(|s| s.set_scroll_y(enabled))
    }

    /// Controls whether this view can scroll horizontally.
    ///
    /// Defaults to `false`.
    ///
    /// Chainable variant.
    pub fn scroll_x(self, enabled: bool) -> Self {
        self.with(|s| s.set_scroll_x(enabled))
    }

    /// Returns for each axis if we are scrolling.
    fn is_scrolling(&self) -> XY<bool> {
        self.inner_size.zip_map(self.last_size, |i, s| i > s)
    }

    /// Stops grabbing the scrollbar.
    fn release_grab(&mut self) {
        self.thumb_grab = None;
    }

    /// Returns the size taken by the scrollbars.
    ///
    /// Will be zero in axis where we're not scrolling.
    fn scrollbar_size(&self) -> Vec2 {
        self.is_scrolling()
            .select_or(self.scrollbar_padding + (1, 1), Vec2::zero())
    }

    /// Returns the size available for the child view.
    fn available_size(&self) -> Vec2 {
        self.last_size - self.scrollbar_size()
    }
}

impl<V> ScrollView<V>
where
    V: View,
{
    /// Compute the size we would need.
    ///
    /// Given the constraints, and the axis that need scrollbars.
    ///
    /// Returns `(inner_size, size, scrollable)`.
    fn sizes_when_scrolling(
        &mut self, constraint: Vec2, scrollable: XY<bool>,
    ) -> (Vec2, Vec2, XY<bool>) {
        // This is the size taken by the scrollbars.
        let scrollbar_size = scrollable
            .select_or(self.scrollbar_padding + (1, 1), Vec2::zero());

        let available = constraint.saturating_sub(scrollbar_size);

        // This the ideal size for the child. May not be what he gets.
        let inner_size = self.inner.required_size(available);

        // Where we're "enabled", accept the constraints.
        // Where we're not, just forward inner_size.
        let size = self.enabled.select_or(
            Vec2::min(inner_size + scrollbar_size, constraint),
            inner_size + scrollbar_size,
        );

        // On non-scrolling axis, give inner_size the available space instead.
        let inner_size =
            self.enabled.select_or(inner_size, size - scrollbar_size);

        let new_scrollable = inner_size.zip_map(size, |i, s| i > s);

        (inner_size, size, new_scrollable)
    }

    /// Starts scrolling from the cursor position.
    ///
    /// Returns `true` if the event was consumed.
    fn start_drag(&mut self, position: Vec2) -> bool {
        let scrollbar_pos = self.last_size - (1, 1);

        let grabbed = scrollbar_pos.zip_map(position, |s, p| s == p);

        let lengths = self.scrollbar_thumb_lengths();
        let offsets = self.scrollbar_thumb_offsets(lengths);

        // See if we grabbed one of the scrollbars
        for (orientation, pos, length, offset) in
            XY::zip4(Orientation::pair(), position, lengths, offsets).zip(grabbed.swap())
                .into_iter()
                .filter(|&(_, grab)| grab)
                .map(|(x, _)| x)
        {

            if pos >= offset && pos < offset + length {
                // We grabbed the thumb! Now scroll from that position.
                self.thumb_grab = Some((orientation, pos - offset));
            } else {
                // We hit the scrollbar, outside of the thumb. Let's move the middle there.
                self.thumb_grab = Some((orientation, (length-1)/2));
                self.drag(position);
            }

            return true;
        }

        false
    }

    fn drag(&mut self, position: Vec2) {
        if let Some((orientation, grab)) = self.thumb_grab {
            self.scroll_to_thumb(
                orientation,
                position.get(orientation).saturating_sub(grab),
            );
        }
    }

    fn scroll_to_thumb(&mut self, orientation: Orientation, thumb_pos: usize) {
        let lengths = self.scrollbar_thumb_lengths();
        let available = self.available_size();

        // The new offset is thumb_pos * (content + 1 - available) / (available + 1 - thumb size)
        let new_offset = (self.inner_size + (1, 1) - available) * thumb_pos
            / (available + (1, 1) - lengths);
        let max_offset = self.inner_size - self.available_size();
        self.offset
            .set_axis_from(orientation, &new_offset.or_min(max_offset));
    }

    /// Computes the size we would need given the constraints.
    ///
    /// First be optimistic and try without scrollbars.
    /// Then try with scrollbars if needed.
    /// Then try again in case we now need to scroll both ways (!!!)
    ///
    /// Returns `(inner_size, size)`
    fn sizes(&mut self, constraint: Vec2) -> (Vec2, Vec2) {
        let (inner_size, size, scrollable) =
            self.sizes_when_scrolling(constraint, XY::new(false, false));

        // If we need to add scrollbars, the available size will change.
        if scrollable.any() && self.show_scrollbars {
            // Attempt 2: he wants to scroll? Sure! Try again with some space for the scrollbar.
            let (inner_size, size, new_scrollable) =
                self.sizes_when_scrolling(constraint, scrollable);
            if scrollable != new_scrollable {
                // Again? We're now scrolling in a new direction?
                // There is no end to this!
                let (inner_size, size, _) =
                    self.sizes_when_scrolling(constraint, new_scrollable);

                // That's enough. If the inner view changed again, ignore it!
                // That'll teach it.
                (inner_size, size)
            } else {
                // Yup, scrolling did it. We're goot to go now.
                (inner_size, size)
            }
        } else {
            // We're not showing any scrollbar, either because we don't scroll
            // or because scrollbars are hidden.
            (inner_size, size)
        }
    }

    fn scrollbar_thumb_lengths(&self) -> Vec2 {
        let available = self.available_size();
        (available * available / self.inner_size).or_max((1, 1))
    }

    fn scrollbar_thumb_offsets(&self, lengths: Vec2) -> Vec2 {
        let available = self.available_size();
        // The number of steps is 1 + the "extra space"
        let steps = available - lengths + (1, 1);
        steps * self.offset / (self.inner_size + (1, 1) - available)
    }
}

impl<V> View for ScrollView<V>
where
    V: View,
{
    fn draw(&self, printer: &Printer) {
        // Draw scrollbar?
        let scrolling = self.is_scrolling();

        let lengths = self.scrollbar_thumb_lengths();
        let offsets = self.scrollbar_thumb_offsets(lengths);

        let line_c = XY::new("-", "|");

        let color = if printer.focused {
            ColorStyle::highlight()
        } else {
            ColorStyle::highlight_inactive()
        };

        let size = self.available_size();

        // TODO: use a more generic zip_all or something?
        XY::zip5(lengths, offsets, size, line_c, Orientation::pair()).run_if(
            scrolling,
            |(length, offset, size, c, orientation)| {
                let start = (printer.size - (1, 1)).with_axis(orientation, 0);
                let offset = orientation.make_vec(offset, 0);

                printer.print_line(orientation, start, size, c);

                let thumb_c = if self.thumb_grab.map(|(o, _)| o == orientation).unwrap_or(false) {
                    " "
                } else {
                    "▒"
                };
                printer.with_color(color, |printer| {
                    printer.print_line(
                        orientation,
                        start + offset,
                        length,
thumb_c,
                    );
                });
            },
        );

        if scrolling.both() {
            printer.print((printer.size.x - 1, printer.size.y - 1), "╳");
        }

        // Draw content
        let printer = printer
            .cropped(size)
            .content_offset(self.offset)
            .inner_size(self.inner_size);
        self.inner.draw(&printer);
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        // Relativize event accorging to the offset
        let mut relative_event = event.clone();
        // eprintln!("Mouse = {:?}", relative_event);
        if let Some(pos) = relative_event.mouse_position_mut() {
            *pos = *pos + self.offset;
        }
        match self.inner.on_event(relative_event) {
            EventResult::Ignored => {
                // If it's an arrow, try to scroll in the given direction.
                // If it's a mouse scroll, try to scroll as well.
                // Also allow Ctrl+arrow to move the view without moving selection.
                match event {
                    Event::Mouse {
                        event: MouseEvent::WheelUp,
                        ..
                    } if self.enabled.y && self.offset.y > 0 =>
                    {
                        self.offset.y = self.offset.y.saturating_sub(3);
                        EventResult::Consumed(None)
                    }
                    Event::Mouse {
                        event: MouseEvent::WheelDown,
                        ..
                    } if self.enabled.y
                        && (self.offset.y + self.available_size().y
                            < self.inner_size.y) =>
                    {
                        self.offset.y = min(
                            self.inner_size
                                .y
                                .saturating_sub(self.available_size().y),
                            self.offset.y + 3,
                        );
                        EventResult::Consumed(None)
                    }
                    Event::Mouse {
                        event: MouseEvent::Press(MouseButton::Left),
                        position,
                        offset,
                    } if position
                        .checked_sub(offset)
                        .map(|position| self.start_drag(position))
                        .unwrap_or(false) =>
                    {
                        EventResult::Consumed(None)
                    }
                    Event::Mouse {
                        event: MouseEvent::Hold(MouseButton::Left),
                        position,
                        offset,
                    } => {
                        let position = position.saturating_sub(offset);
                        self.drag(position);
                        EventResult::Consumed(None)
                    }
                    Event::Mouse {
                        event: MouseEvent::Release(MouseButton::Left),
                        ..
                    } => {
                        self.release_grab();
                        EventResult::Consumed(None)
                    }
                    Event::Ctrl(Key::Up) | Event::Key(Key::Up)
                        if self.enabled.y && self.offset.y > 0 =>
                    {
                        self.offset.y -= 1;
                        EventResult::Consumed(None)
                    }
                    Event::Ctrl(Key::Down) | Event::Key(Key::Down)
                        if self.enabled.y
                            && (self.offset.y + self.available_size().y
                                < self.inner_size.y) =>
                    {
                        self.offset.y += 1;
                        EventResult::Consumed(None)
                    }
                    Event::Ctrl(Key::Left) | Event::Key(Key::Left)
                        if self.enabled.x && self.offset.x > 0 =>
                    {
                        self.offset.x -= 1;
                        EventResult::Consumed(None)
                    }
                    Event::Ctrl(Key::Right) | Event::Key(Key::Right)
                        if self.enabled.x
                            && (self.offset.x + self.available_size().x
                                < self.inner_size.x) =>
                    {
                        self.offset.x += 1;
                        EventResult::Consumed(None)
                    }
                    _ => EventResult::Ignored,
                }
            }
            other => {
                // Fix offset?
                let important = self.inner.important_area(self.inner_size);

                // The furthest top-left we can go
                let top_left = (important.bottom_right() + (1, 1))
                    .saturating_sub(self.available_size());
                // The furthest bottom-right we can go
                let bottom_right = important.top_left();

                // "top_left < bottom_right" is NOT guaranteed
                // if the child is larger than the view.
                let offset_min = Vec2::min(top_left, bottom_right);
                let offset_max = Vec2::max(top_left, bottom_right);

                self.offset =
                    self.offset.or_max(offset_min).or_min(offset_max);

                other
            }
        }
    }

    fn layout(&mut self, size: Vec2) {
        // Size is final now
        self.last_size = size;

        let (inner_size, _) = self.sizes(size);

        // Ask one more time
        self.inner_size = inner_size;

        self.inner.layout(self.inner_size);

        // TODO: Refresh offset if needed!
    }

    fn needs_relayout(&self) -> bool {
        self.inner.needs_relayout()
    }

    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        // Attempt 1: try without scrollbars
        let (_, size) = self.sizes(constraint);

        size
    }

    fn call_on_any<'a>(&mut self, selector: &Selector, cb: AnyCb<'a>) {
        self.inner.call_on_any(selector, cb)
    }

    fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
        self.inner.focus_view(selector)
    }

    fn take_focus(&mut self, source: Direction) -> bool {
        let is_scrollable = self.is_scrolling().any();
        self.inner.take_focus(source) || is_scrollable
    }
}
<commit_msg>Fix panics from overflow subtraction<commit_after>use direction::{Direction, Orientation};
use event::{AnyCb, Event, EventResult, Key, MouseButton, MouseEvent};
use rect::Rect;
use theme::ColorStyle;
use vec::Vec2;
use view::{Selector, View};
use xy::XY;
use Printer;
use With;

use std::cmp::min;

/// Wraps a view in a scrollable area.
pub struct ScrollView<V> {
    // The wrapped view.
    inner: V,

    // This is the size the child thinks we're giving him.
    inner_size: Vec2,

    // Offset into the inner view.
    //
    // Our `(0,0)` will be inner's `offset`
    offset: Vec2,

    // What was our own size last time we checked.
    //
    // This includes scrollbars, if any.
    last_size: Vec2,

    // Are we scrollable in each direction?
    enabled: XY<bool>,

    // Should we show scrollbars?
    //
    // Even if this is true, no scrollbar will be printed if we don't need to
    // scroll.
    //
    // TODO: have an option to always show the scrollbar.
    // TODO: have an option to show scrollbar on top/left.
    show_scrollbars: bool,

    // How much padding should be between content and scrollbar?
    scrollbar_padding: Vec2,

    /// Initial position of the cursor when dragging.
    thumb_grab: Option<(Orientation, usize)>,
}

impl<V> ScrollView<V> {
    /// Creates a new ScrollView around `view`.
    pub fn new(view: V) -> Self {
        ScrollView {
            inner: view,
            inner_size: Vec2::zero(),
            offset: Vec2::zero(),
            last_size: Vec2::zero(),
            enabled: XY::new(false, true),
            show_scrollbars: true,
            scrollbar_padding: Vec2::new(1, 0),
            thumb_grab: None,
        }
    }

    /// Returns the viewport in the inner content.
    pub fn content_viewport(&self) -> Rect {
        Rect::from_size(self.offset, self.available_size())
    }

    /// Sets the scroll offset to the given value
    pub fn set_offset<S>(&mut self, offset: S)
    where
        S: Into<Vec2>,
    {
        let max_offset = self.inner_size.saturating_sub(self.available_size());
        self.offset = offset.into().or_min(max_offset);
    }

    /// Controls whether this view can scroll vertically.
    ///
    /// Defaults to `true`.
    pub fn set_scroll_y(&mut self, enabled: bool) {
        self.enabled.y = enabled;
    }

    /// Controls whether this view can scroll horizontally.
    ///
    /// Defaults to `false`.
    pub fn set_scroll_x(&mut self, enabled: bool) {
        self.enabled.x = enabled;
    }

    /// Controls whether this view can scroll vertically.
    ///
    /// Defaults to `true`.
    ///
    /// Chainable variant.
    pub fn scroll_y(self, enabled: bool) -> Self {
        self.with(|s| s.set_scroll_y(enabled))
    }

    /// Controls whether this view can scroll horizontally.
    ///
    /// Defaults to `false`.
    ///
    /// Chainable variant.
    pub fn scroll_x(self, enabled: bool) -> Self {
        self.with(|s| s.set_scroll_x(enabled))
    }

    /// Returns for each axis if we are scrolling.
    fn is_scrolling(&self) -> XY<bool> {
        self.inner_size.zip_map(self.last_size, |i, s| i > s)
    }

    /// Stops grabbing the scrollbar.
    fn release_grab(&mut self) {
        self.thumb_grab = None;
    }

    /// Returns the size taken by the scrollbars.
    ///
    /// Will be zero in axis where we're not scrolling.
    fn scrollbar_size(&self) -> Vec2 {
        self.is_scrolling()
            .select_or(self.scrollbar_padding + (1, 1), Vec2::zero())
    }

    /// Returns the size available for the child view.
    fn available_size(&self) -> Vec2 {
        self.last_size.saturating_sub(self.scrollbar_size())
    }
}

impl<V> ScrollView<V>
where
    V: View,
{
    /// Compute the size we would need.
    ///
    /// Given the constraints, and the axis that need scrollbars.
    ///
    /// Returns `(inner_size, size, scrollable)`.
    fn sizes_when_scrolling(
        &mut self, constraint: Vec2, scrollable: XY<bool>,
    ) -> (Vec2, Vec2, XY<bool>) {
        // This is the size taken by the scrollbars.
        let scrollbar_size = scrollable
            .select_or(self.scrollbar_padding + (1, 1), Vec2::zero());

        let available = constraint.saturating_sub(scrollbar_size);

        // This the ideal size for the child. May not be what he gets.
        let inner_size = self.inner.required_size(available);

        // Where we're "enabled", accept the constraints.
        // Where we're not, just forward inner_size.
        let size = self.enabled.select_or(
            Vec2::min(inner_size + scrollbar_size, constraint),
            inner_size + scrollbar_size,
        );

        // On non-scrolling axis, give inner_size the available space instead.
        let inner_size = self
            .enabled
            .select_or(inner_size, size.saturating_sub(scrollbar_size));

        let new_scrollable = inner_size.zip_map(size, |i, s| i > s);

        (inner_size, size, new_scrollable)
    }

    /// Starts scrolling from the cursor position.
    ///
    /// Returns `true` if the event was consumed.
    fn start_drag(&mut self, position: Vec2) -> bool {
        let scrollbar_pos = self.last_size.saturating_sub((1, 1));

        let grabbed = scrollbar_pos.zip_map(position, |s, p| s == p);

        let lengths = self.scrollbar_thumb_lengths();
        let offsets = self.scrollbar_thumb_offsets(lengths);

        // See if we grabbed one of the scrollbars
        for (orientation, pos, length, offset) in
            XY::zip4(Orientation::pair(), position, lengths, offsets)
                .zip(grabbed.swap())
                .into_iter()
                .filter(|&(_, grab)| grab)
                .map(|(x, _)| x)
        {
            if pos >= offset && pos < offset + length {
                // We grabbed the thumb! Now scroll from that position.
                self.thumb_grab = Some((orientation, pos - offset));
            } else {
                // We hit the scrollbar, outside of the thumb.
                // Let's move the middle there.
                self.thumb_grab = Some((orientation, (length - 1) / 2));
                self.drag(position);
            }

            return true;
        }

        false
    }

    fn drag(&mut self, position: Vec2) {
        if let Some((orientation, grab)) = self.thumb_grab {
            self.scroll_to_thumb(
                orientation,
                position.get(orientation).saturating_sub(grab),
            );
        }
    }

    fn scroll_to_thumb(&mut self, orientation: Orientation, thumb_pos: usize) {
        let lengths = self.scrollbar_thumb_lengths();
        let available = self.available_size();

        // The new offset is:
        // thumb_pos * (content + 1 - available) / (available + 1 - thumb size)
        let new_offset = (self.inner_size + (1, 1)).saturating_sub(available)
            * thumb_pos
            / (available + (1, 1)).saturating_sub(lengths);
        let max_offset = self.inner_size.saturating_sub(self.available_size());
        self.offset
            .set_axis_from(orientation, &new_offset.or_min(max_offset));
    }

    /// Computes the size we would need given the constraints.
    ///
    /// First be optimistic and try without scrollbars.
    /// Then try with scrollbars if needed.
    /// Then try again in case we now need to scroll both ways (!!!)
    ///
    /// Returns `(inner_size, size)`
    fn sizes(&mut self, constraint: Vec2) -> (Vec2, Vec2) {
        let (inner_size, size, scrollable) =
            self.sizes_when_scrolling(constraint, XY::new(false, false));

        // If we need to add scrollbars, the available size will change.
        if scrollable.any() && self.show_scrollbars {
            // Attempt 2: he wants to scroll? Sure!
            // Try again with some space for the scrollbar.
            let (inner_size, size, new_scrollable) =
                self.sizes_when_scrolling(constraint, scrollable);
            if scrollable != new_scrollable {
                // Again? We're now scrolling in a new direction?
                // There is no end to this!
                let (inner_size, size, _) =
                    self.sizes_when_scrolling(constraint, new_scrollable);

                // That's enough. If the inner view changed again, ignore it!
                // That'll teach it.
                (inner_size, size)
            } else {
                // Yup, scrolling did it. We're goot to go now.
                (inner_size, size)
            }
        } else {
            // We're not showing any scrollbar, either because we don't scroll
            // or because scrollbars are hidden.
            (inner_size, size)
        }
    }

    fn scrollbar_thumb_lengths(&self) -> Vec2 {
        let available = self.available_size();
        (available * available / self.inner_size).or_max((1, 1))
    }

    fn scrollbar_thumb_offsets(&self, lengths: Vec2) -> Vec2 {
        let available = self.available_size();
        // The number of steps is 1 + the "extra space"
        let steps = (available + (1, 1)).saturating_sub(lengths);
        steps * self.offset / (self.inner_size + (1, 1) - available)
    }
}

impl<V> View for ScrollView<V>
where
    V: View,
{
    fn draw(&self, printer: &Printer) {
        // Draw scrollbar?
        let scrolling = self.is_scrolling();

        let lengths = self.scrollbar_thumb_lengths();
        let offsets = self.scrollbar_thumb_offsets(lengths);

        let line_c = XY::new("-", "|");

        let color = if printer.focused {
            ColorStyle::highlight()
        } else {
            ColorStyle::highlight_inactive()
        };

        let size = self.available_size();

        // TODO: use a more generic zip_all or something?
        XY::zip5(lengths, offsets, size, line_c, Orientation::pair()).run_if(
            scrolling,
            |(length, offset, size, c, orientation)| {
                let start = printer
                    .size
                    .saturating_sub((1, 1))
                    .with_axis(orientation, 0);
                let offset = orientation.make_vec(offset, 0);

                printer.print_line(orientation, start, size, c);

                let thumb_c = if self
                    .thumb_grab
                    .map(|(o, _)| o == orientation)
                    .unwrap_or(false)
                {
                    " "
                } else {
                    "▒"
                };
                printer.with_color(color, |printer| {
                    printer.print_line(
                        orientation,
                        start + offset,
                        length,
                        thumb_c,
                    );
                });
            },
        );

        if scrolling.both() {
            printer.print(printer.size.saturating_sub((1, 1)), "╳");
        }

        // Draw content
        let printer = printer
            .cropped(size)
            .content_offset(self.offset)
            .inner_size(self.inner_size);
        self.inner.draw(&printer);
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        // Relativize event accorging to the offset
        let mut relative_event = event.clone();
        // eprintln!("Mouse = {:?}", relative_event);
        if let Some(pos) = relative_event.mouse_position_mut() {
            *pos = *pos + self.offset;
        }
        match self.inner.on_event(relative_event) {
            EventResult::Ignored => {
                // If it's an arrow, try to scroll in the given direction.
                // If it's a mouse scroll, try to scroll as well.
                // Also allow Ctrl+arrow to move the view,
                // but not the selection.
                match event {
                    Event::Mouse {
                        event: MouseEvent::WheelUp,
                        ..
                    } if self.enabled.y && self.offset.y > 0 =>
                    {
                        self.offset.y = self.offset.y.saturating_sub(3);
                        EventResult::Consumed(None)
                    }
                    Event::Mouse {
                        event: MouseEvent::WheelDown,
                        ..
                    } if self.enabled.y
                        && (self.offset.y + self.available_size().y
                            < self.inner_size.y) =>
                    {
                        self.offset.y = min(
                            self.inner_size
                                .y
                                .saturating_sub(self.available_size().y),
                            self.offset.y + 3,
                        );
                        EventResult::Consumed(None)
                    }
                    Event::Mouse {
                        event: MouseEvent::Press(MouseButton::Left),
                        position,
                        offset,
                    } if position
                        .checked_sub(offset)
                        .map(|position| self.start_drag(position))
                        .unwrap_or(false) =>
                    {
                        EventResult::Consumed(None)
                    }
                    Event::Mouse {
                        event: MouseEvent::Hold(MouseButton::Left),
                        position,
                        offset,
                    } => {
                        let position = position.saturating_sub(offset);
                        self.drag(position);
                        EventResult::Consumed(None)
                    }
                    Event::Mouse {
                        event: MouseEvent::Release(MouseButton::Left),
                        ..
                    } => {
                        self.release_grab();
                        EventResult::Consumed(None)
                    }
                    Event::Ctrl(Key::Up) | Event::Key(Key::Up)
                        if self.enabled.y && self.offset.y > 0 =>
                    {
                        self.offset.y -= 1;
                        EventResult::Consumed(None)
                    }
                    Event::Ctrl(Key::Down) | Event::Key(Key::Down)
                        if self.enabled.y
                            && (self.offset.y + self.available_size().y
                                < self.inner_size.y) =>
                    {
                        self.offset.y += 1;
                        EventResult::Consumed(None)
                    }
                    Event::Ctrl(Key::Left) | Event::Key(Key::Left)
                        if self.enabled.x && self.offset.x > 0 =>
                    {
                        self.offset.x -= 1;
                        EventResult::Consumed(None)
                    }
                    Event::Ctrl(Key::Right) | Event::Key(Key::Right)
                        if self.enabled.x
                            && (self.offset.x + self.available_size().x
                                < self.inner_size.x) =>
                    {
                        self.offset.x += 1;
                        EventResult::Consumed(None)
                    }
                    _ => EventResult::Ignored,
                }
            }
            other => {
                // Fix offset?
                let important = self.inner.important_area(self.inner_size);

                // The furthest top-left we can go
                let top_left = (important.bottom_right() + (1, 1))
                    .saturating_sub(self.available_size());
                // The furthest bottom-right we can go
                let bottom_right = important.top_left();

                // "top_left < bottom_right" is NOT guaranteed
                // if the child is larger than the view.
                let offset_min = Vec2::min(top_left, bottom_right);
                let offset_max = Vec2::max(top_left, bottom_right);

                self.offset =
                    self.offset.or_max(offset_min).or_min(offset_max);

                other
            }
        }
    }

    fn layout(&mut self, size: Vec2) {
        // Size is final now
        self.last_size = size;

        let (inner_size, _) = self.sizes(size);

        // Ask one more time
        self.inner_size = inner_size;

        self.inner.layout(self.inner_size);

        // The offset cannot be more than content - available
        self.offset = self
            .offset
            .or_min(inner_size.saturating_sub(self.available_size()));
    }

    fn needs_relayout(&self) -> bool {
        self.inner.needs_relayout()
    }

    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        // Attempt 1: try without scrollbars
        let (_, size) = self.sizes(constraint);

        size
    }

    fn call_on_any<'a>(&mut self, selector: &Selector, cb: AnyCb<'a>) {
        self.inner.call_on_any(selector, cb)
    }

    fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
        self.inner.focus_view(selector)
    }

    fn take_focus(&mut self, source: Direction) -> bool {
        let is_scrollable = self.is_scrolling().any();
        self.inner.take_focus(source) || is_scrollable
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::{slice, mem};

use libc::{size_t, c_int, c_uint};

// Bounded package merge algorithm, based on the paper
// "A Fast and Space-Economical Algorithm for Length-Limited Coding
// Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

#[derive(Debug)]
struct Node {
    weight: size_t,
    leaf_counts: Vec<c_int>,
}

#[derive(Debug)]
struct Leaf {
    pub weight: size_t,
    pub index: size_t,
}
impl PartialEq for Leaf {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}
impl Eq for Leaf { }
impl Ord for Leaf {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.cmp(&other.weight)
    }
}
impl PartialOrd for Leaf {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[derive(Debug)]
struct List {
    lookahead1: Node,
    lookahead2: Node,
    next_leaf_index: size_t,
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliLengthLimitedCodeLengths(frequencies: *const size_t, n: c_int, maxbits: c_int, bitlengths: *mut c_uint) -> c_int {
    let freqs = unsafe { slice::from_raw_parts(frequencies, n as usize) };
    let result = length_limited_code_lengths(freqs, maxbits);

    for (i, res) in result.into_iter().enumerate() {
        unsafe {
            *bitlengths.offset(i as isize) = res as c_uint;
        }
    }

    return 0;
}

pub fn length_limited_code_lengths(frequencies: &[size_t], maxbits: c_int) -> Vec<size_t> {
    let mut leaves = vec![];

    // Count used symbols and place them in the leaves.
    for (i, &freq) in frequencies.iter().enumerate() {
        if freq != 0 {
            leaves.push(Leaf { weight: freq, index: i });
        }
    }

    // Short circuit some special cases
    if leaves.is_empty() {
        // There are no non-zero frequencies.
        return vec![0; frequencies.len()];
    }
    if leaves.len() == 1 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        return result;
    }
    if leaves.len() == 2 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        result[leaves[1].index] = 1;
        return result;
    }

    // Sort the leaves from least frequent to most frequent.
    // Add index into the same variable for stable sorting.
    for leaf in leaves.iter_mut() {
        leaf.weight = (leaf.weight << 9) | leaf.index;
    }
    leaves.sort();
    for leaf in leaves.iter_mut() {
        leaf.weight >>= 9;
    }

    let mut lists = Vec::with_capacity(maxbits as usize);
    for _ in 0..maxbits {
        lists.push(List {
            lookahead1: Node { weight: leaves[0].weight, leaf_counts: vec![1] },
            lookahead2: Node { weight: leaves[1].weight, leaf_counts: vec![2] },
            next_leaf_index: 2,
        });
    }

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each boundary_pm run creates one.
    let num_boundary_pm_runs = 2 * leaves.len() - 4;
    for _ in 0..num_boundary_pm_runs {
        lists = boundary_pm(lists, &leaves);
    }

    let n = frequencies.len();
    let mut result = vec![0; n];

    let mut a = lists.pop().unwrap().lookahead2.leaf_counts.into_iter().rev().peekable();

    let mut bitlength_value = 1;
    while let Some(leaf_count) = a.next() {
        let next_count = *a.peek().unwrap_or(&0);
        for i in next_count..leaf_count {
            result[leaves[i as usize].index as usize] = bitlength_value;
        }
        bitlength_value += 1;
    }

    result
}

fn lowest_list(mut current_list: List, mut lists: Vec<List>, leaves: &Vec<Leaf>) -> Vec<List> {
    // We're in the lowest list, just add another leaf to the lookaheads
    // There will always be more leaves to be added on level 0 so this is safe.
    let ref next_leaf = leaves[current_list.next_leaf_index];
    current_list.lookahead2 = Node {
        weight: next_leaf.weight,
        leaf_counts: vec![current_list.lookahead1.leaf_counts.last().unwrap() + 1],
    };
    current_list.next_leaf_index += 1;
    lists.push(current_list);
    lists
}

fn next_leaf(mut current_list: List, previous_list: List, mut lists: Vec<List>, leaves: &Vec<Leaf>) -> Vec<List> {
    // The next leaf goes next; counting itself makes the leaf_count increase by one.
    let mut last_leaf_counts = current_list.lookahead1.leaf_counts.clone();
    let mut last_count = last_leaf_counts.pop().unwrap();
    last_count += 1;
    last_leaf_counts.push(last_count);
    current_list.lookahead2 = Node {
        weight: leaves[current_list.next_leaf_index].weight,
        leaf_counts: last_leaf_counts,
    };
    current_list.next_leaf_index += 1;
    lists.push(previous_list);
    lists.push(current_list);
    lists
}

fn next_tree(mut current_list: List, previous_list: List, weight_sum: size_t, mut lists: Vec<List>, leaves: &Vec<Leaf>) -> Vec<List> {
    // Make a tree from the lookaheads from the previous list; that goes next.
    // This is not a leaf node, so the leaf count stays the same.
    let mut last_leaf_counts = previous_list.lookahead2.leaf_counts.clone();
    last_leaf_counts.push(*current_list.lookahead1.leaf_counts.last().unwrap());
    current_list.lookahead2 = Node {
        weight: weight_sum,
        leaf_counts: last_leaf_counts,
    };
    // The previous list needs two new lookahead nodes.
    lists.push(previous_list);
    lists = boundary_pm(lists, leaves);
    lists = boundary_pm(lists, leaves);
    lists.push(current_list);

    lists
}

fn boundary_pm(mut lists: Vec<List>, leaves: &Vec<Leaf>) -> Vec<List> {
    let mut current_list = lists.pop().unwrap();
    if lists.is_empty() && current_list.next_leaf_index == leaves.len() {
        // We've added all the leaves to the lowest list, so we're done here
        lists.push(current_list);
        return lists;
    }

    mem::swap(&mut current_list.lookahead1, &mut current_list.lookahead2);

    if lists.is_empty() {
        lists = lowest_list(current_list, lists, leaves);
    } else {
        // We're at a list other than the lowest list.
        let previous_list = lists.pop().unwrap();
        let weight_sum = previous_list.lookahead1.weight + previous_list.lookahead2.weight;

        if current_list.next_leaf_index < leaves.len() && weight_sum > leaves[current_list.next_leaf_index].weight {
            lists = next_leaf(current_list, previous_list, lists, leaves);
        } else {
            lists = next_tree(current_list, previous_list, weight_sum, lists, leaves);
        }
    }

    lists
}

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

    #[test]
    fn test_from_paper_3() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 3);
        let answer = vec![3, 3, 3, 3, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn test_from_paper_4() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 4);
        let answer = vec![4, 4, 3, 2, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_7() {
        let input = [252, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![1, 0, 6, 4, 3, 3, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_15() {
        let input = [0, 0, 0, 0, 0, 0, 18, 0, 6, 0, 12, 2, 14, 9, 27, 15, 23, 15, 17, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 15);
        let answer = vec! [0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 4, 6, 4, 4, 3, 4, 3, 3, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn no_frequencies() {
        let input = [0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_one_frequency() {
        let input = [0, 10, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 1, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_two_frequencies() {
        let input = [0, 0, 0, 0, 252, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }
}
<commit_msg>Reduce allocations in lowest_list and next_tree<commit_after>use std::cmp::Ordering;
use std::{slice, mem};

use libc::{size_t, c_int, c_uint};

// Bounded package merge algorithm, based on the paper
// "A Fast and Space-Economical Algorithm for Length-Limited Coding
// Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

#[derive(Debug)]
struct Node {
    weight: size_t,
    leaf_counts: Vec<c_int>,
}

#[derive(Debug)]
struct Leaf {
    pub weight: size_t,
    pub index: size_t,
}
impl PartialEq for Leaf {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}
impl Eq for Leaf { }
impl Ord for Leaf {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.cmp(&other.weight)
    }
}
impl PartialOrd for Leaf {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[derive(Debug)]
struct List {
    lookahead1: Node,
    lookahead2: Node,
    next_leaf_index: size_t,
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliLengthLimitedCodeLengths(frequencies: *const size_t, n: c_int, maxbits: c_int, bitlengths: *mut c_uint) -> c_int {
    let freqs = unsafe { slice::from_raw_parts(frequencies, n as usize) };
    let result = length_limited_code_lengths(freqs, maxbits);

    for (i, res) in result.into_iter().enumerate() {
        unsafe {
            *bitlengths.offset(i as isize) = res as c_uint;
        }
    }

    return 0;
}

pub fn length_limited_code_lengths(frequencies: &[size_t], maxbits: c_int) -> Vec<size_t> {
    let mut leaves = vec![];

    // Count used symbols and place them in the leaves.
    for (i, &freq) in frequencies.iter().enumerate() {
        if freq != 0 {
            leaves.push(Leaf { weight: freq, index: i });
        }
    }

    // Short circuit some special cases
    if leaves.is_empty() {
        // There are no non-zero frequencies.
        return vec![0; frequencies.len()];
    }
    if leaves.len() == 1 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        return result;
    }
    if leaves.len() == 2 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        result[leaves[1].index] = 1;
        return result;
    }

    // Sort the leaves from least frequent to most frequent.
    // Add index into the same variable for stable sorting.
    for leaf in leaves.iter_mut() {
        leaf.weight = (leaf.weight << 9) | leaf.index;
    }
    leaves.sort();
    for leaf in leaves.iter_mut() {
        leaf.weight >>= 9;
    }

    let mut lists = Vec::with_capacity(maxbits as usize);
    for _ in 0..maxbits {
        lists.push(List {
            lookahead1: Node { weight: leaves[0].weight, leaf_counts: vec![1] },
            lookahead2: Node { weight: leaves[1].weight, leaf_counts: vec![2] },
            next_leaf_index: 2,
        });
    }

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each boundary_pm run creates one.
    let num_boundary_pm_runs = 2 * leaves.len() - 4;
    for _ in 0..num_boundary_pm_runs {
        lists = boundary_pm(lists, &leaves);
    }

    let n = frequencies.len();
    let mut result = vec![0; n];

    let mut a = lists.pop().unwrap().lookahead2.leaf_counts.into_iter().rev().peekable();

    let mut bitlength_value = 1;
    while let Some(leaf_count) = a.next() {
        let next_count = *a.peek().unwrap_or(&0);
        for i in next_count..leaf_count {
            result[leaves[i as usize].index as usize] = bitlength_value;
        }
        bitlength_value += 1;
    }

    result
}

fn lowest_list(mut current_list: List, mut lists: Vec<List>, leaves: &Vec<Leaf>) -> Vec<List> {
    // We're in the lowest list, just add another leaf to the lookaheads
    // There will always be more leaves to be added on level 0 so this is safe.
    let ref next_leaf = leaves[current_list.next_leaf_index];
    current_list.lookahead2.weight = next_leaf.weight;

    current_list.lookahead2.leaf_counts[0] = current_list.lookahead1.leaf_counts.last().unwrap() + 1;
    current_list.next_leaf_index += 1;
    lists.push(current_list);
    lists
}

fn next_leaf(mut current_list: List, previous_list: List, mut lists: Vec<List>, leaves: &Vec<Leaf>) -> Vec<List> {
    // The next leaf goes next; counting itself makes the leaf_count increase by one.
    let mut last_leaf_counts = current_list.lookahead1.leaf_counts.clone();
    let mut last_count = last_leaf_counts.pop().unwrap();
    last_count += 1;
    last_leaf_counts.push(last_count);
    current_list.lookahead2 = Node {
        weight: leaves[current_list.next_leaf_index].weight,
        leaf_counts: last_leaf_counts,
    };
    current_list.next_leaf_index += 1;
    lists.push(previous_list);
    lists.push(current_list);
    lists
}

fn next_tree(mut current_list: List, previous_list: List, weight_sum: size_t, mut lists: Vec<List>, leaves: &Vec<Leaf>) -> Vec<List> {
    // Make a tree from the lookaheads from the previous list; that goes next.
    // This is not a leaf node, so the leaf count stays the same.

    current_list.lookahead2.weight = weight_sum;
    current_list.lookahead2.leaf_counts.clear();
    current_list.lookahead2.leaf_counts.extend(previous_list.lookahead2.leaf_counts.iter());
    current_list.lookahead2.leaf_counts.push(*current_list.lookahead1.leaf_counts.last().unwrap());

    // The previous list needs two new lookahead nodes.
    lists.push(previous_list);
    lists = boundary_pm(lists, leaves);
    lists = boundary_pm(lists, leaves);
    lists.push(current_list);

    lists
}

fn boundary_pm(mut lists: Vec<List>, leaves: &Vec<Leaf>) -> Vec<List> {
    let mut current_list = lists.pop().unwrap();
    if lists.is_empty() && current_list.next_leaf_index == leaves.len() {
        // We've added all the leaves to the lowest list, so we're done here
        lists.push(current_list);
        return lists;
    }

    mem::swap(&mut current_list.lookahead1, &mut current_list.lookahead2);

    if lists.is_empty() {
        lists = lowest_list(current_list, lists, leaves);
    } else {
        // We're at a list other than the lowest list.
        let previous_list = lists.pop().unwrap();
        let weight_sum = previous_list.lookahead1.weight + previous_list.lookahead2.weight;

        if current_list.next_leaf_index < leaves.len() && weight_sum > leaves[current_list.next_leaf_index].weight {
            lists = next_leaf(current_list, previous_list, lists, leaves);
        } else {
            lists = next_tree(current_list, previous_list, weight_sum, lists, leaves);
        }
    }

    lists
}

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

    #[test]
    fn test_from_paper_3() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 3);
        let answer = vec![3, 3, 3, 3, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn test_from_paper_4() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 4);
        let answer = vec![4, 4, 3, 2, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_7() {
        let input = [252, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![1, 0, 6, 4, 3, 3, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_15() {
        let input = [0, 0, 0, 0, 0, 0, 18, 0, 6, 0, 12, 2, 14, 9, 27, 15, 23, 15, 17, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 15);
        let answer = vec! [0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 4, 6, 4, 4, 3, 4, 3, 3, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn no_frequencies() {
        let input = [0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_one_frequency() {
        let input = [0, 10, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 1, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_two_frequencies() {
        let input = [0, 0, 0, 0, 252, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }
}
<|endoftext|>


<commit_before>use ::syntax::ext::base::{Annotatable, ExtCtxt, MacResult, MacEager};
use ::syntax::ast::{MetaItem};
use ::syntax::codemap::{Span};
use easy_plugin::{PluginResult};

use ::syntax::util::small_vector::{SmallVector};

pub fn resource_struct_def_decorator(
    cx: &mut ExtCtxt,
    span: Span,
    _meta_item: &MetaItem,
    annotatable: &Annotatable,
    push: &mut FnMut(Annotatable)
) {
    let builder = ::aster::AstBuilder::new().span(span);

    let struct_ident = annotatable.clone().expect_item().ident;
    let struct_ident_str = &*struct_ident.name.as_str();
    let type_field_name = builder.id(["_rustler_nif_struct_type_", struct_ident_str].concat());
    let dtor_name_ident = builder.id(["_rustler_nif_struct_type_dtor_", struct_ident_str].concat());

    // Static field for Nif Resource Type (Set in resource_struct_init!)
    push(Annotatable::Item(quote_item!(cx,
        #[allow(non_snake_case)]
        static mut $type_field_name: Option<::rustler::resource::NifResourceType<$struct_ident>> = None;
    ).unwrap()));

    // Destructor for the type
    push(Annotatable::Item(quote_item!(cx,
        #[allow(non_snake_case)]
        extern "C" fn $dtor_name_ident(env: ::rustler::codegen_runtime::NIF_ENV,
                                       obj: ::rustler::codegen_runtime::MUTABLE_NIF_RESOURCE_HANDLE) {
            unsafe { ::rustler::codegen_runtime::handle_drop_resource_struct_handle::<$struct_ident>(env, obj) }
        }
    ).unwrap()));

    // NifResourceTypeProvider trait implementation
    push(Annotatable::Item(quote_item!(cx,
        impl ::rustler::resource::NifResourceTypeProvider for $struct_ident {
            fn get_dtor() -> extern "C" fn(_env: ::rustler::codegen_runtime::NIF_ENV,
                                           handle: ::rustler::codegen_runtime::MUTABLE_NIF_RESOURCE_HANDLE) {
                $dtor_name_ident
            }
            fn get_type<'a>() -> &'a ::rustler::resource::NifResourceType<Self> {
                unsafe { &$type_field_name }.as_ref().unwrap()
            }
            unsafe fn set_type(typ: ::rustler::resource::NifResourceType<Self>) {
                $type_field_name = Some(typ)
            }
        }
    ).unwrap()));
}

easy_plugin! {
    struct Arguments { $struct_ident:ident, $env:ident }

    pub fn resource_struct_init_macro(cx: &mut ExtCtxt, _span: Span, arguments: Arguments) -> PluginResult<Box<MacResult>> {
        let builder = ::aster::AstBuilder::new();

        let env_ident = arguments.env;
        let struct_ident = arguments.struct_ident;
        let struct_ident_str_lit = builder.lit().str(struct_ident.node.name.as_str());
        //let type_field_name_ident = builder.id(["_rustler_nif_struct_type_", struct_ident_str].concat());
        let init_item = quote_stmt!(cx, {
            let res = match ::rustler::resource::open_struct_resource_type::<$struct_ident>($env_ident, $struct_ident_str_lit,
                                                                                ::rustler::codegen_runtime::NIF_RESOURCE_FLAGS::ERL_NIF_RT_CREATE) {
                Some(inner) => inner,
                None => {
                    println!("Failiure in creating resource type");
                    return false;
                }
            };
            unsafe {
                use ::rustler::resource::NifResourceTypeProvider;
                $struct_ident::set_type(res);
            };
        }).unwrap();

        Ok(MacEager::stmts(SmallVector::one(init_item)))
    }
}
<commit_msg>remove dead code<commit_after><|endoftext|>


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

#[doc = "Rust wrappers around the raw JS apis"];

use std::libc::types::os::arch::c95::{size_t, c_uint};
use std::libc::{c_char, uintptr_t};
use std::hashmap::HashMap;
use jsapi::*;
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use JSVAL_NULL;
use ERR;
use name_pool::*;
use global::global_class;
use std::ptr;
use std::ptr::null;
use result;
use result_obj;
use std::uint;
use std::str::raw::from_c_str;
use std::cast;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

pub type rt = @rt_rsrc;

pub struct rt_rsrc {
    ptr : *JSRuntime,
}

impl Drop for rt_rsrc {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            JS_Finish(self.ptr);
        }
    }
}

pub fn new_runtime(p: *JSRuntime) -> rt {
    return @rt_rsrc {
        ptr: p
    }
}

impl rt_rsrc {
    #[fixed_stack_segment]
    pub fn cx(@self) -> @Cx {
        unsafe {
            new_context(JS_NewContext(self.ptr, default_stacksize as size_t), self)
        }
    }
}

// FIXME: Is this safe once we have more than one stack segment?
extern fn gc_callback(rt: *JSRuntime, _status: JSGCStatus) {
    use std::rt::local::Local;
    use std::rt::task::Task;
    unsafe {
        do Local::borrow |task: &mut Task| {
            match task.coroutine {
                Some(ref c) => {
                    let start = c.current_stack_segment.start() as uintptr_t;
                    let end = c.current_stack_segment.end() as uintptr_t;
                    JS_SetNativeStackBounds(rt, uint::min(start, end), uint::max(start, end));
                }
                None => {
                    fail!("gc_callback: cannot get stack for JS_SetNativeStackBounds");
                }
            }
        }
    }
}

#[fixed_stack_segment]
pub fn rt() -> rt {
    unsafe {
        let runtime = JS_Init(default_heapsize);
        JS_SetGCCallback(runtime, gc_callback);
        return new_runtime(runtime);
    }
}

// ___________________________________________________________________________
// contexts

pub struct Cx {
    ptr: *JSContext,
    rt: rt,
    classes: @mut HashMap<~str, @JSClass>,
}

#[unsafe_destructor]
impl Drop for Cx {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            JS_DestroyContext(self.ptr);
        }
    }
}

pub fn new_context(ptr: *JSContext, rt: rt) -> @Cx {
    return @Cx {
        ptr: ptr,
        rt: rt,
        classes: @mut HashMap::new()
    }
}

impl Cx {
    #[fixed_stack_segment]
    pub fn rooted_obj(@self, obj: *JSObject) -> jsobj {
        let jsobj = @jsobj_rsrc {cx: self, cxptr: self.ptr, ptr: obj};
        unsafe {
            JS_AddObjectRoot(self.ptr, ptr::to_unsafe_ptr(&jsobj.ptr));
        }
        jsobj
    }

    pub fn set_default_options_and_version(@self) {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT |
                         JSOPTION_TYPE_INFERENCE);
        self.set_version(JSVERSION_LATEST);
    }

    #[fixed_stack_segment]
    pub fn set_options(@self, v: c_uint) {
        unsafe {
            JS_SetOptions(self.ptr, v);
        }
    }

    #[fixed_stack_segment]
    pub fn set_version(@self, v: i32) {
        unsafe {
            JS_SetVersion(self.ptr, v);
        }
    }

    #[fixed_stack_segment]
    pub fn set_logging_error_reporter(@self) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportError);
        }
    }

    #[fixed_stack_segment]
    pub fn set_error_reporter(@self, reportfn: extern "C" fn(*JSContext, *c_char, *JSErrorReport)) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportfn);
        }
    }

    #[fixed_stack_segment]
    pub fn new_compartment(@self,
                       globclsfn: &fn(@mut NamePool) -> JSClass)
                    -> Result<@mut Compartment,()> {
        unsafe {
            let np = NamePool();
            let globcls = @globclsfn(np);
            let globobj = JS_NewGlobalObject(self.ptr, ptr::to_unsafe_ptr(&*globcls), null());
            result(JS_InitStandardClasses(self.ptr, globobj)).chain(|_ok| {
                let compartment = @mut Compartment {
                    cx: self,
                    name_pool: np,
                    global_funcs: ~[],
                    global_props: ~[],
                    global_class: globcls,
                    global_obj: self.rooted_obj(globobj),
                    global_protos: @mut HashMap::new()
                };
                self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
                Ok(compartment)
            })
        }
    }

    #[fixed_stack_segment]
    pub fn new_compartment_with_global(@self, global: *JSObject) -> Result<@mut Compartment,()> {
        let np = NamePool();
        let compartment = @mut Compartment {
            cx: self,
            name_pool: np,
            global_funcs: ~[],
            global_props: ~[],
            global_class: @global_class(np),
            global_obj: self.rooted_obj(global),
            global_protos: @mut HashMap::new()
        };
        unsafe {
            self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
        }
        Ok(compartment)
    }

    #[fixed_stack_segment]
    pub fn evaluate_script(@self, glob: jsobj, bytes: ~[u8], filename: ~str, line_num: uint)
                    -> Result<(),()> {
        do bytes.as_imm_buf |bytes_ptr, bytes_len| {
            do filename.to_c_str().with_ref |filename_cstr| {
                let bytes_ptr = bytes_ptr as *c_char;
                let rval: JSVal = JSVAL_NULL;
                debug!("Evaluating script from %s with bytes %?", filename, bytes);
                unsafe {
                    if JS_EvaluateScript(self.ptr, glob.ptr,
                                         bytes_ptr, bytes_len as c_uint,
                                         filename_cstr, line_num as c_uint,
                                         ptr::to_unsafe_ptr(&rval)) == ERR {
                        debug!("...err!");
                        Err(())
                    } else {
                        // we could return the script result but then we'd have
                        // to root it and so forth and, really, who cares?
                        debug!("...ok!");
                        Ok(())
                    }
                }
            }
        }
    }

    pub fn lookup_class_name(@self, s: ~str) ->  @JSClass {
        // FIXME: expect should really take a lambda...
        let error_msg = fmt!("class %s not found in class table", s);
        let name = self.classes.find(&s);
        *(name.expect(error_msg))
    }

    #[fixed_stack_segment]
    pub unsafe fn get_cx_private(@self) -> *() {
        cast::transmute(JS_GetContextPrivate(self.ptr))
    }

    #[fixed_stack_segment]
    pub unsafe fn set_cx_private(@self, data: *()) {
        JS_SetContextPrivate(self.ptr, cast::transmute(data));
    }

    #[fixed_stack_segment]
    pub unsafe fn get_obj_private(@self, obj: *JSObject) -> *() {
        cast::transmute(JS_GetPrivate(obj))
    }

    #[fixed_stack_segment]
    pub unsafe fn set_obj_private(@self, obj: *JSObject, data: *()) {
        JS_SetPrivate(obj, cast::transmute(data));
    }
}

pub extern fn reportError(_cx: *JSContext, msg: *c_char, report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {~"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        error!("Error at %s:%?: %s\n", fname, lineno, msg);
    }
}

// ___________________________________________________________________________
// compartment

pub struct Compartment {
    cx: @Cx,
    name_pool: @mut NamePool,
    global_funcs: ~[@~[JSFunctionSpec]],
    global_props: ~[@~[JSPropertySpec]],
    global_class: @JSClass,
    global_obj: jsobj,
    global_protos: @mut HashMap<~str, jsobj>
}

impl Compartment {
    #[fixed_stack_segment]
    pub fn define_functions(@mut self,
                        specfn: &fn(@mut NamePool) -> ~[JSFunctionSpec])
                     -> Result<(),()> {
        let specvec = @specfn(self.name_pool);
        self.global_funcs.push(specvec);
        do specvec.as_imm_buf |specs, _len| {
            unsafe {
                result(JS_DefineFunctions(self.cx.ptr, self.global_obj.ptr, specs))
            }
        }
    }
    #[fixed_stack_segment]
    pub fn define_properties(@mut self, specfn: &fn() -> ~[JSPropertySpec]) -> Result<(),()> {
        let specvec = @specfn();
        self.global_props.push(specvec);
        do specvec.as_imm_buf |specs, _len| {
            unsafe {
                result(JS_DefineProperties(self.cx.ptr, self.global_obj.ptr, specs))
            }
        }
    }
    #[fixed_stack_segment]
    pub fn define_property(@mut self,
                       name: ~str,
                       value: JSVal,
                       getter: JSPropertyOp, setter: JSStrictPropertyOp,
                       attrs: c_uint)
                    -> Result<(),()> {
        unsafe {
            result(JS_DefineProperty(self.cx.ptr,
                                     self.global_obj.ptr,
                                     self.add_name(name),
                                     value,
                                     Some(getter),
                                     Some(setter),
                                     attrs))
        }
    }
    #[fixed_stack_segment]
    pub fn new_object(@mut self, class_name: ~str, proto: *JSObject, parent: *JSObject)
               -> Result<jsobj, ()> {
        unsafe {
            let classptr = self.cx.lookup_class_name(class_name);
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, &*classptr, proto, parent));
            result_obj(obj)
        }
    }
    #[fixed_stack_segment]
    pub fn new_object_with_proto(@mut self, class_name: ~str, proto_name: ~str, parent: *JSObject)
                          -> Result<jsobj, ()> {
        let classptr = self.cx.lookup_class_name(class_name);
        let proto = self.global_protos.find(&proto_name.clone()).expect(
            fmt!("new_object_with_proto: expected to find %s in the proto \
                  table", proto_name));
        unsafe {
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, ptr::to_unsafe_ptr(&*classptr),
                                                      proto.ptr, parent));
            result_obj(obj)
        }
    }
    pub fn get_global_proto(@mut self, name: ~str) -> jsobj {
        let proto = self.global_protos.get(&name);
        *proto
    }
    pub fn stash_global_proto(@mut self, name: ~str, proto: jsobj) {
        let global_protos = self.global_protos;
        if !global_protos.insert(name, proto) {
            fail!(~"Duplicate global prototype registered; you're gonna have a bad time.")
        }
    }
    pub fn register_class(@mut self, class_fn: &fn(x: @mut Compartment) -> JSClass) {
        let classptr = @class_fn(self);
        if !self.cx.classes.insert(
            unsafe { from_c_str(classptr.name) },
            classptr) {
            fail!(~"Duplicate JSClass registered; you're gonna have a bad time.")
        }
    }
    pub fn add_name(@mut self, name: ~str) -> *c_char {
        self.name_pool.add(name.clone())
    }
}

// ___________________________________________________________________________
// objects

pub type jsobj = @jsobj_rsrc;

pub struct jsobj_rsrc {
    cx: @Cx,
    cxptr: *JSContext,
    ptr: *JSObject,
}

#[unsafe_destructor]
impl Drop for jsobj_rsrc {
    #[fixed_stack_segment]
    fn drop(&self) {
        unsafe {
            JS_RemoveObjectRoot(self.cxptr, ptr::to_unsafe_ptr(&self.ptr));
        }
    }
}

impl jsobj_rsrc {
    fn new_object(&self, cx: @Cx, cxptr: *JSContext, ptr: *JSObject) -> jsobj {
        return @jsobj_rsrc {
            cx: cx,
            cxptr: cxptr,
            ptr: ptr
        }
    }
}

// ___________________________________________________________________________
// random utilities

pub trait to_jsstr {
    fn to_jsstr(self, cx: @Cx) -> *JSString;
}

impl to_jsstr for ~str {
    #[fixed_stack_segment]
    fn to_jsstr(self, cx: @Cx) -> *JSString {
        do self.as_imm_buf |buf, len| {
            unsafe {
                let cbuf = cast::transmute(buf);
                JS_NewStringCopyN(cx.ptr, cbuf, len as size_t)
            }
        }
    }
}

#[cfg(test)]
pub mod test {
    use super::rt;
    use super::super::global;
    use super::super::jsapi::{JS_GC, JS_GetRuntime};

    #[test]
    #[fixed_stack_segment]
    pub fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.set_default_options_and_version();
        cx.set_logging_error_reporter();
        cx.new_compartment(global::global_class).chain(|comp| {
            unsafe { JS_GC(JS_GetRuntime(comp.cx.ptr)); }

            comp.define_functions(global::debug_fns);

            let s = ~"debug(22);";
            let bytes = s.as_bytes().to_owned();
            cx.evaluate_script(comp.global_obj, bytes, ~"test", 1u)
        });
    }

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

#[doc = "Rust wrappers around the raw JS apis"];

use std::libc::types::os::arch::c95::{size_t, c_uint};
use std::libc::{c_char, uintptr_t};
use std::hashmap::HashMap;
use jsapi::*;
use default_stacksize;
use default_heapsize;
use JSOPTION_VAROBJFIX;
use JSOPTION_METHODJIT;
use JSOPTION_TYPE_INFERENCE;
use JSVAL_NULL;
use ERR;
use name_pool::*;
use global::global_class;
use std::ptr;
use std::ptr::null;
use result;
use result_obj;
use std::uint;
use std::str::raw::from_c_str;
use std::cast;

// ___________________________________________________________________________
// friendly Rustic API to runtimes

pub type rt = @rt_rsrc;

pub struct rt_rsrc {
    ptr : *JSRuntime,
}

impl Drop for rt_rsrc {
    #[fixed_stack_segment]
    fn drop(&mut self) {
        unsafe {
            JS_Finish(self.ptr);
        }
    }
}

pub fn new_runtime(p: *JSRuntime) -> rt {
    return @rt_rsrc {
        ptr: p
    }
}

impl rt_rsrc {
    #[fixed_stack_segment]
    pub fn cx(@self) -> @Cx {
        unsafe {
            new_context(JS_NewContext(self.ptr, default_stacksize as size_t), self)
        }
    }
}

// FIXME: Is this safe once we have more than one stack segment?
extern fn gc_callback(rt: *JSRuntime, _status: JSGCStatus) {
    use std::rt::local::Local;
    use std::rt::task::Task;
    unsafe {
        do Local::borrow |task: &mut Task| {
            match task.coroutine {
                Some(ref c) => {
                    let start = c.current_stack_segment.start() as uintptr_t;
                    let end = c.current_stack_segment.end() as uintptr_t;
                    JS_SetNativeStackBounds(rt, uint::min(start, end), uint::max(start, end));
                }
                None => {
                    fail!("gc_callback: cannot get stack for JS_SetNativeStackBounds");
                }
            }
        }
    }
}

#[fixed_stack_segment]
pub fn rt() -> rt {
    unsafe {
        let runtime = JS_Init(default_heapsize);
        JS_SetGCCallback(runtime, gc_callback);
        return new_runtime(runtime);
    }
}

// ___________________________________________________________________________
// contexts

pub struct Cx {
    ptr: *JSContext,
    rt: rt,
    classes: @mut HashMap<~str, @JSClass>,
}

#[unsafe_destructor]
impl Drop for Cx {
    #[fixed_stack_segment]
    fn drop(&mut self) {
        unsafe {
            JS_DestroyContext(self.ptr);
        }
    }
}

pub fn new_context(ptr: *JSContext, rt: rt) -> @Cx {
    return @Cx {
        ptr: ptr,
        rt: rt,
        classes: @mut HashMap::new()
    }
}

impl Cx {
    #[fixed_stack_segment]
    pub fn rooted_obj(@self, obj: *JSObject) -> jsobj {
        let jsobj = @jsobj_rsrc {cx: self, cxptr: self.ptr, ptr: obj};
        unsafe {
            JS_AddObjectRoot(self.ptr, ptr::to_unsafe_ptr(&jsobj.ptr));
        }
        jsobj
    }

    pub fn set_default_options_and_version(@self) {
        self.set_options(JSOPTION_VAROBJFIX | JSOPTION_METHODJIT |
                         JSOPTION_TYPE_INFERENCE);
        self.set_version(JSVERSION_LATEST);
    }

    #[fixed_stack_segment]
    pub fn set_options(@self, v: c_uint) {
        unsafe {
            JS_SetOptions(self.ptr, v);
        }
    }

    #[fixed_stack_segment]
    pub fn set_version(@self, v: i32) {
        unsafe {
            JS_SetVersion(self.ptr, v);
        }
    }

    #[fixed_stack_segment]
    pub fn set_logging_error_reporter(@self) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportError);
        }
    }

    #[fixed_stack_segment]
    pub fn set_error_reporter(@self, reportfn: extern "C" fn(*JSContext, *c_char, *JSErrorReport)) {
        unsafe {
            JS_SetErrorReporter(self.ptr, reportfn);
        }
    }

    #[fixed_stack_segment]
    pub fn new_compartment(@self,
                       globclsfn: &fn(@mut NamePool) -> JSClass)
                    -> Result<@mut Compartment,()> {
        unsafe {
            let np = NamePool();
            let globcls = @globclsfn(np);
            let globobj = JS_NewGlobalObject(self.ptr, ptr::to_unsafe_ptr(&*globcls), null());
            result(JS_InitStandardClasses(self.ptr, globobj)).and_then(|_ok| {
                let compartment = @mut Compartment {
                    cx: self,
                    name_pool: np,
                    global_funcs: ~[],
                    global_props: ~[],
                    global_class: globcls,
                    global_obj: self.rooted_obj(globobj),
                    global_protos: @mut HashMap::new()
                };
                self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
                Ok(compartment)
            })
        }
    }

    #[fixed_stack_segment]
    pub fn new_compartment_with_global(@self, global: *JSObject) -> Result<@mut Compartment,()> {
        let np = NamePool();
        let compartment = @mut Compartment {
            cx: self,
            name_pool: np,
            global_funcs: ~[],
            global_props: ~[],
            global_class: @global_class(np),
            global_obj: self.rooted_obj(global),
            global_protos: @mut HashMap::new()
        };
        unsafe {
            self.set_cx_private(ptr::to_unsafe_ptr(&*compartment) as *());
        }
        Ok(compartment)
    }

    #[fixed_stack_segment]
    pub fn evaluate_script(@self, glob: jsobj, bytes: ~[u8], filename: ~str, line_num: uint)
                    -> Result<(),()> {
        do bytes.as_imm_buf |bytes_ptr, bytes_len| {
            do filename.to_c_str().with_ref |filename_cstr| {
                let bytes_ptr = bytes_ptr as *c_char;
                let rval: JSVal = JSVAL_NULL;
                debug!("Evaluating script from %s with bytes %?", filename, bytes);
                unsafe {
                    if JS_EvaluateScript(self.ptr, glob.ptr,
                                         bytes_ptr, bytes_len as c_uint,
                                         filename_cstr, line_num as c_uint,
                                         ptr::to_unsafe_ptr(&rval)) == ERR {
                        debug!("...err!");
                        Err(())
                    } else {
                        // we could return the script result but then we'd have
                        // to root it and so forth and, really, who cares?
                        debug!("...ok!");
                        Ok(())
                    }
                }
            }
        }
    }

    pub fn lookup_class_name(@self, s: ~str) ->  @JSClass {
        // FIXME: expect should really take a lambda...
        let error_msg = fmt!("class %s not found in class table", s);
        let name = self.classes.find(&s);
        *(name.expect(error_msg))
    }

    #[fixed_stack_segment]
    pub unsafe fn get_cx_private(@self) -> *() {
        cast::transmute(JS_GetContextPrivate(self.ptr))
    }

    #[fixed_stack_segment]
    pub unsafe fn set_cx_private(@self, data: *()) {
        JS_SetContextPrivate(self.ptr, cast::transmute(data));
    }

    #[fixed_stack_segment]
    pub unsafe fn get_obj_private(@self, obj: *JSObject) -> *() {
        cast::transmute(JS_GetPrivate(obj))
    }

    #[fixed_stack_segment]
    pub unsafe fn set_obj_private(@self, obj: *JSObject, data: *()) {
        JS_SetPrivate(obj, cast::transmute(data));
    }
}

pub extern fn reportError(_cx: *JSContext, msg: *c_char, report: *JSErrorReport) {
    unsafe {
        let fnptr = (*report).filename;
        let fname = if fnptr.is_not_null() {from_c_str(fnptr)} else {~"none"};
        let lineno = (*report).lineno;
        let msg = from_c_str(msg);
        error!("Error at %s:%?: %s\n", fname, lineno, msg);
    }
}

// ___________________________________________________________________________
// compartment

pub struct Compartment {
    cx: @Cx,
    name_pool: @mut NamePool,
    global_funcs: ~[@~[JSFunctionSpec]],
    global_props: ~[@~[JSPropertySpec]],
    global_class: @JSClass,
    global_obj: jsobj,
    global_protos: @mut HashMap<~str, jsobj>
}

impl Compartment {
    #[fixed_stack_segment]
    pub fn define_functions(@mut self,
                        specfn: &fn(@mut NamePool) -> ~[JSFunctionSpec])
                     -> Result<(),()> {
        let specvec = @specfn(self.name_pool);
        self.global_funcs.push(specvec);
        do specvec.as_imm_buf |specs, _len| {
            unsafe {
                result(JS_DefineFunctions(self.cx.ptr, self.global_obj.ptr, specs))
            }
        }
    }
    #[fixed_stack_segment]
    pub fn define_properties(@mut self, specfn: &fn() -> ~[JSPropertySpec]) -> Result<(),()> {
        let specvec = @specfn();
        self.global_props.push(specvec);
        do specvec.as_imm_buf |specs, _len| {
            unsafe {
                result(JS_DefineProperties(self.cx.ptr, self.global_obj.ptr, specs))
            }
        }
    }
    #[fixed_stack_segment]
    pub fn define_property(@mut self,
                       name: ~str,
                       value: JSVal,
                       getter: JSPropertyOp, setter: JSStrictPropertyOp,
                       attrs: c_uint)
                    -> Result<(),()> {
        unsafe {
            result(JS_DefineProperty(self.cx.ptr,
                                     self.global_obj.ptr,
                                     self.add_name(name),
                                     value,
                                     Some(getter),
                                     Some(setter),
                                     attrs))
        }
    }
    #[fixed_stack_segment]
    pub fn new_object(@mut self, class_name: ~str, proto: *JSObject, parent: *JSObject)
               -> Result<jsobj, ()> {
        unsafe {
            let classptr = self.cx.lookup_class_name(class_name);
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, &*classptr, proto, parent));
            result_obj(obj)
        }
    }
    #[fixed_stack_segment]
    pub fn new_object_with_proto(@mut self, class_name: ~str, proto_name: ~str, parent: *JSObject)
                          -> Result<jsobj, ()> {
        let classptr = self.cx.lookup_class_name(class_name);
        let proto = self.global_protos.find(&proto_name.clone()).expect(
            fmt!("new_object_with_proto: expected to find %s in the proto \
                  table", proto_name));
        unsafe {
            let obj = self.cx.rooted_obj(JS_NewObject(self.cx.ptr, ptr::to_unsafe_ptr(&*classptr),
                                                      proto.ptr, parent));
            result_obj(obj)
        }
    }
    pub fn get_global_proto(@mut self, name: ~str) -> jsobj {
        let proto = self.global_protos.get(&name);
        *proto
    }
    pub fn stash_global_proto(@mut self, name: ~str, proto: jsobj) {
        let global_protos = self.global_protos;
        if !global_protos.insert(name, proto) {
            fail!(~"Duplicate global prototype registered; you're gonna have a bad time.")
        }
    }
    pub fn register_class(@mut self, class_fn: &fn(x: @mut Compartment) -> JSClass) {
        let classptr = @class_fn(self);
        if !self.cx.classes.insert(
            unsafe { from_c_str(classptr.name) },
            classptr) {
            fail!(~"Duplicate JSClass registered; you're gonna have a bad time.")
        }
    }
    pub fn add_name(@mut self, name: ~str) -> *c_char {
        self.name_pool.add(name.clone())
    }
}

// ___________________________________________________________________________
// objects

pub type jsobj = @jsobj_rsrc;

pub struct jsobj_rsrc {
    cx: @Cx,
    cxptr: *JSContext,
    ptr: *JSObject,
}

#[unsafe_destructor]
impl Drop for jsobj_rsrc {
    #[fixed_stack_segment]
    fn drop(&mut self) {
        unsafe {
            JS_RemoveObjectRoot(self.cxptr, ptr::to_unsafe_ptr(&self.ptr));
        }
    }
}

impl jsobj_rsrc {
    fn new_object(&self, cx: @Cx, cxptr: *JSContext, ptr: *JSObject) -> jsobj {
        return @jsobj_rsrc {
            cx: cx,
            cxptr: cxptr,
            ptr: ptr
        }
    }
}

// ___________________________________________________________________________
// random utilities

pub trait to_jsstr {
    fn to_jsstr(self, cx: @Cx) -> *JSString;
}

impl to_jsstr for ~str {
    #[fixed_stack_segment]
    fn to_jsstr(self, cx: @Cx) -> *JSString {
        do self.as_imm_buf |buf, len| {
            unsafe {
                let cbuf = cast::transmute(buf);
                JS_NewStringCopyN(cx.ptr, cbuf, len as size_t)
            }
        }
    }
}

#[cfg(test)]
pub mod test {
    use super::rt;
    use super::super::global;
    use super::super::jsapi::{JS_GC, JS_GetRuntime};

    #[test]
    #[fixed_stack_segment]
    pub fn dummy() {
        let rt = rt();
        let cx = rt.cx();
        cx.set_default_options_and_version();
        cx.set_logging_error_reporter();
        cx.new_compartment(global::global_class).and_then(|comp| {
            unsafe { JS_GC(JS_GetRuntime(comp.cx.ptr)); }

            comp.define_functions(global::debug_fns);

            let s = ~"debug(22);";
            let bytes = s.as_bytes().to_owned();
            cx.evaluate_script(comp.global_obj, bytes, ~"test", 1u)
        });
    }

}
<|endoftext|>


<commit_before>#![feature(lang_items)]
#![no_std]
#![feature(const_fn)]
#![feature(unique)]
#![feature(asm)]
#![feature(naked_functions)]

extern crate rlibc;
extern crate volatile;
extern crate spin;

use rlibc::memset;

mod initium;
#[macro_use]
mod vga_buffer;
mod version;

/// Magic number passed to the entry point of a Initium kernel.
static INITIUM_MAGIC: u32 = 0xb007cafe;

/// This is the entry point for the Kernel, all the things must be initialized
#[no_mangle]
pub extern "C" fn start(magic: u32, initium_info_addr: usize) -> ! {
    extern "C" {
        /// The starting byte of the .bss segment
        static mut __bss_start: u8;
        /// The ending byte of the .bss segment
        static mut __bss_end: u8;
    }

    // Zero BSS, this initializes statics that are set to 0
    unsafe {
        let start_ptr = &mut __bss_start as *mut u8;
        let end_ptr = &mut __bss_end as *const u8 as usize;

        if start_ptr as usize <= end_ptr {
            let size = end_ptr - start_ptr as usize;
            memset(start_ptr, 0, size);
        }
    }

    // clear the screen
    vga_buffer::clear_screen();

    // Print a Welcome message
    println!("Infinity OS {}", version::PULSAR_VER_STRING);

    // print out the Initium Magic number
    println!("Bootloder magic flag: 0x{0:x}", magic);

    // check if we are been booted up from a valid bootloader
    if magic != INITIUM_MAGIC {
        panic!("Invalid magic flag!");
    }

    // load boot information
    let boot_info = unsafe { initium::load(initium_info_addr) };

    // get the free memory areas and print it out
    let memory_map_tag = boot_info.memory_map();
    println!("Memory Areas:");
    for entry in memory_map_tag {
        println!("    start: 0x{:x}, length: 0x{:x}",
        entry.base_address(), entry.length());
    }

    // Print the kernel sections
    let elf_sections = boot_info.elf_sections().expect("Elf-sections tag required");
    println!("Kernel Sections:");
    for section in elf_sections.elf_sections() {
        println!("    addr: 0x{:x}, size: 0x{:x}, flags: 0x{:x}",
                 section.section_start_address(), section.size_bytes(), section.flags());
    }

    // Makes a infinity loop to avoid the kernel returns
    loop {}
}

#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn _Unwind_Resume() -> ! {
    loop {}
}

#[lang = "eh_personality"]
extern "C" fn eh_personality() {}

#[lang = "panic_fmt"]
#[no_mangle]
pub extern "C" fn panic_fmt() -> ! {
    loop {}
}
<commit_msg>Show file and line on the panic function<commit_after>#![feature(lang_items)]
#![no_std]
#![feature(const_fn)]
#![feature(unique)]
#![feature(asm)]
#![feature(naked_functions)]

extern crate rlibc;
extern crate volatile;
extern crate spin;

use rlibc::memset;

mod initium;
#[macro_use]
mod vga_buffer;
mod version;

/// Magic number passed to the entry point of a Initium kernel.
static INITIUM_MAGIC: u32 = 0xb007cafe;

/// This is the entry point for the Kernel, all the things must be initialized
#[no_mangle]
pub extern "C" fn start(magic: u32, initium_info_addr: usize) -> ! {
    extern "C" {
        /// The starting byte of the .bss segment
        static mut __bss_start: u8;
        /// The ending byte of the .bss segment
        static mut __bss_end: u8;
    }

    // Zero BSS, this initializes statics that are set to 0
    unsafe {
        let start_ptr = &mut __bss_start as *mut u8;
        let end_ptr = &mut __bss_end as *const u8 as usize;

        if start_ptr as usize <= end_ptr {
            let size = end_ptr - start_ptr as usize;
            memset(start_ptr, 0, size);
        }
    }

    // clear the screen
    vga_buffer::clear_screen();

    // Print a Welcome message
    println!("Infinity OS {}", version::PULSAR_VER_STRING);

    // print out the Initium Magic number
    println!("Bootloder magic flag: 0x{0:x}", magic);

    // check if we are been booted up from a valid bootloader
    if magic != INITIUM_MAGIC {
        panic!("Invalid magic flag!");
    }

    // load boot information
    let boot_info = unsafe { initium::load(initium_info_addr) };

    // get the free memory areas and print it out
    let memory_map_tag = boot_info.memory_map();
    println!("Memory Areas:");
    for entry in memory_map_tag {
        println!("    start: 0x{:x}, length: 0x{:x}",
        entry.base_address(), entry.length());
    }

    // Print the kernel sections
    let elf_sections = boot_info.elf_sections().expect("Elf-sections tag required");
    println!("Kernel Sections:");
    for section in elf_sections.elf_sections() {
        println!("    addr: 0x{:x}, size: 0x{:x}, flags: 0x{:x}",
                 section.section_start_address(), section.size_bytes(), section.flags());
    }

    // Makes a infinity loop to avoid the kernel returns
    loop {}
}

#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn _Unwind_Resume() -> ! {
    loop {}
}

#[lang = "eh_personality"]
extern "C" fn eh_personality() {}

#[lang = "panic_fmt"]
#[no_mangle]
pub extern fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str,
                        line: u32) -> !
{
    println!("\n\nPANIC in {} at line {}:", file, line);
    println!("    {}", fmt);
    loop{}
}
<|endoftext|>


<commit_before>use std::ascii::StrAsciiExt;

// This is an implementation of "natural sort order". See
// http://blog.codinghorror.com/sorting-for-humans-natural-sort-order/
// for more information and examples. It tries to sort "9" before
// "10", which makes sense to those regular human types.

// It works by splitting an input string into several parts, and then
// comparing based on those parts. A SortPart derives TotalOrd, so a
// Vec<SortPart> will automatically have natural sorting.

#[deriving(Eq, Ord, PartialEq, PartialOrd)]
pub enum SortPart {
    Numeric(u64),
    Stringular(String),
}

impl SortPart {
    pub fn from_string(is_digit: bool, slice: &str) -> SortPart {
        if is_digit {
            Numeric(from_str::<u64>(slice).expect(slice))
        } else {
            Stringular(slice.to_ascii_lower())
        }
    }

    // The logic here is taken from my question at
    // http://stackoverflow.com/q/23969191/3484614

    pub fn split_into_parts(input: &str) -> Vec<SortPart> {
        let mut parts = vec![];

        if input.is_empty() {
            return parts
        }

        let mut is_digit = input.char_at(0).is_digit();
        let mut start = 0;

        for (i, c) in input.char_indices() {
            if is_digit != c.is_digit() {
                parts.push(SortPart::from_string(is_digit, input.slice(start, i)));
                is_digit = !is_digit;
                start = i;
            }
        }

        parts.push(SortPart::from_string(is_digit, input.slice_from(start)));
        parts
    }
}
<commit_msg>Fix bug in sort where numbers could get too long<commit_after>use std::ascii::StrAsciiExt;

// This is an implementation of "natural sort order". See
// http://blog.codinghorror.com/sorting-for-humans-natural-sort-order/
// for more information and examples. It tries to sort "9" before
// "10", which makes sense to those regular human types.

// It works by splitting an input string into several parts, and then
// comparing based on those parts. A SortPart derives TotalOrd, so a
// Vec<SortPart> will automatically have natural sorting.

#[deriving(Eq, Ord, PartialEq, PartialOrd)]
pub enum SortPart {
    Numeric(u64),
    Stringular(String),
}

impl SortPart {
    pub fn from_string(is_digit: bool, slice: &str) -> SortPart {
        if is_digit {
            // numbers too big for a u64 fall back into strings.
            match from_str::<u64>(slice) {
                Some(num) => Numeric(num),
                None => Stringular(slice.to_string()),
            }
        } else {
            Stringular(slice.to_ascii_lower())
        }
    }

    // The logic here is taken from my question at
    // http://stackoverflow.com/q/23969191/3484614

    pub fn split_into_parts(input: &str) -> Vec<SortPart> {
        let mut parts = vec![];

        if input.is_empty() {
            return parts
        }

        let mut is_digit = input.char_at(0).is_digit();
        let mut start = 0;

        for (i, c) in input.char_indices() {
            if is_digit != c.is_digit() {
                parts.push(SortPart::from_string(is_digit, input.slice(start, i)));
                is_digit = !is_digit;
                start = i;
            }
        }

        parts.push(SortPart::from_string(is_digit, input.slice_from(start)));
        parts
    }
}

#[test]
fn test_numeric() {
    let bits = SortPart::split_into_parts("123456789".as_slice());
    assert!(bits == vec![ Numeric(123456789) ]);
}

#[test]
fn test_stringular() {
    let bits = SortPart::split_into_parts("toothpaste".as_slice());
    assert!(bits == vec![ Stringular("toothpaste".to_string()) ]);
}

#[test]
fn test_empty() {
    let bits = SortPart::split_into_parts("".as_slice());
    assert!(bits == vec![]);
}

#[test]
fn test_one() {
    let bits = SortPart::split_into_parts("123abc123".as_slice());
    assert!(bits == vec![ Numeric(123), Stringular("abc".to_string()), Numeric(123) ]);
}

#[test]
fn test_two() {
    let bits = SortPart::split_into_parts("final version 3.pdf".as_slice());
    assert!(bits == vec![ Stringular("final version ".to_string()), Numeric(3), Stringular(".pdf".to_string()) ]);
}

#[test]
fn test_huge_number() {
    let bits = SortPart::split_into_parts("9999999999999999999999999999999999999999999999999999999".as_slice());
    assert!(bits == vec![ Stringular("9999999999999999999999999999999999999999999999999999999".to_string()) ]);
}

#[test]
fn test_case() {
    let bits = SortPart::split_into_parts("123ABC123".as_slice());
    assert!(bits == vec![ Numeric(123), Stringular("abc".to_string()), Numeric(123) ]);
}
<|endoftext|>