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

//! Servo heavily uses display lists, which are retained-mode lists of rendering commands to
/// perform. Using a list instead of rendering elements in immediate mode allows transforms, hit
/// testing, and invalidation to be performed using the same primitives as painting. It also allows
/// Servo to aggressively cull invisible and out-of-bounds rendering elements, to reduce overdraw.
/// Finally, display lists allow tiles to be farmed out onto multiple CPUs and rendered in
/// parallel (although this benefit does not apply to GPU-based rendering).
///
/// Display items describe relatively high-level drawing operations (for example, entire borders
/// and shadows instead of lines and blur operations), to reduce the amount of allocation required.
/// They are therefore not exactly analogous to constructs like Skia pictures, which consist of
/// low-level drawing primitives.

use color::Color;
use render_context::RenderContext;
use text::TextRun;

use extra::arc::Arc;
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use servo_net::image::base::Image;
use servo_util::geometry::Au;
use servo_util::range::Range;
use std::cast::transmute_region;
use std::vec::VecIterator;
use style::computed_values::border_style;

/// A list of rendering operations to be performed.
pub struct DisplayList<E> {
    list: ~[DisplayItem<E>]
}

impl<E> DisplayList<E> {
    /// Creates a new display list.
    pub fn new() -> DisplayList<E> {
        DisplayList {
            list: ~[]
        }
    }

    /// Appends the given item to the display list.
    pub fn append_item(&mut self, item: DisplayItem<E>) {
        // FIXME(Issue #150): crashes
        //debug!("Adding display item {:u}: {}", self.len(), item);
        self.list.push(item)
    }

    /// Draws the display list into the given render context.
    pub fn draw_into_context(&self, render_context: &mut RenderContext) {
        debug!("Beginning display list.");
        for item in self.list.iter() {
            // FIXME(Issue #150): crashes
            //debug!("drawing {}", *item);
            item.draw_into_context(render_context)
        }
        debug!("Ending display list.")
    }

    /// Returns a preorder iterator over the given display list.
    pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a,E> {
        ParentDisplayItemIterator(self.list.iter())
    }
}

/// One drawing command in the list.
pub enum DisplayItem<E> {
    SolidColorDisplayItemClass(~SolidColorDisplayItem<E>),
    TextDisplayItemClass(~TextDisplayItem<E>),
    ImageDisplayItemClass(~ImageDisplayItem<E>),
    BorderDisplayItemClass(~BorderDisplayItem<E>),
    ClipDisplayItemClass(~ClipDisplayItem<E>)
}

/// Information common to all display items.
pub struct BaseDisplayItem<E> {
    /// The boundaries of the display item.
    ///
    /// TODO: Which coordinate system should this use?
    bounds: Rect<Au>,

    /// Extra data: either the originating flow (for hit testing) or nothing (for rendering).
    extra: E,
}

/// Renders a solid color.
pub struct SolidColorDisplayItem<E> {
    base: BaseDisplayItem<E>,
    color: Color,
}

/// Renders text.
pub struct TextDisplayItem<E> {
    /// Fields common to all display items.
    base: BaseDisplayItem<E>,

    /// The text run.
    text_run: Arc<~TextRun>,

    /// The range of text within the text run.
    range: Range,

    /// The color of the text.
    text_color: Color,

    /// A bitfield of flags for text display items.
    flags: TextDisplayItemFlags,

    /// The color of text-decorations
    underline_color: Color,
    overline_color: Color,
    line_through_color: Color,
}

/// Flags for text display items.
pub struct TextDisplayItemFlags(u8);

impl TextDisplayItemFlags {
    pub fn new() -> TextDisplayItemFlags {
        TextDisplayItemFlags(0)
    }
}

// Whether underlining is forced on.
bitfield!(TextDisplayItemFlags, override_underline, set_override_underline, 0x01)
// Whether overlining is forced on.
bitfield!(TextDisplayItemFlags, override_overline, set_override_overline, 0x02)
// Whether line-through is forced on.
bitfield!(TextDisplayItemFlags, override_line_through, set_override_line_through, 0x04)

/// Renders an image.
pub struct ImageDisplayItem<E> {
    base: BaseDisplayItem<E>,
    image: Arc<~Image>,
}

/// Renders a border.
pub struct BorderDisplayItem<E> {
    base: BaseDisplayItem<E>,

    /// The border widths
    border: SideOffsets2D<Au>,

    /// The border colors.
    color: SideOffsets2D<Color>,

    /// The border styles.
    style: SideOffsets2D<border_style::T>
}

pub struct ClipDisplayItem<E> {
    base: BaseDisplayItem<E>,
    child_list: ~[DisplayItem<E>],
    need_clip: bool
}

pub enum DisplayItemIterator<'a,E> {
    EmptyDisplayItemIterator,
    ParentDisplayItemIterator(VecIterator<'a,DisplayItem<E>>),
}

impl<'a,E> Iterator<&'a DisplayItem<E>> for DisplayItemIterator<'a,E> {
    #[inline]
    fn next(&mut self) -> Option<&'a DisplayItem<E>> {
        match *self {
            EmptyDisplayItemIterator => None,
            ParentDisplayItemIterator(ref mut subiterator) => subiterator.next(),
        }
    }
}

impl<E> DisplayItem<E> {
    /// Renders this display item into the given render context.
    fn draw_into_context(&self, render_context: &mut RenderContext) {
        match *self {
            SolidColorDisplayItemClass(ref solid_color) => {
                render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
            }

            ClipDisplayItemClass(ref clip) => {
                if clip.need_clip {
                    render_context.draw_push_clip(&clip.base.bounds);
                }
                for item in clip.child_list.iter() {
                    (*item).draw_into_context(render_context);
                }
                if clip.need_clip {
                    render_context.draw_pop_clip();
                }
            }

            TextDisplayItemClass(ref text) => {
                debug!("Drawing text at {:?}.", text.base.bounds);

                // FIXME(pcwalton): Allocating? Why?
                let text_run = text.text_run.get();
                let font = render_context.font_ctx.get_font_by_descriptor(&text_run.font_descriptor).unwrap();

                let font_metrics = font.borrow().with(|font| {
                    font.metrics.clone()
                });
                let origin = text.base.bounds.origin;
                let baseline_origin = Point2D(origin.x, origin.y + font_metrics.ascent);
                font.borrow().with_mut(|font| {
                    font.draw_text_into_context(render_context,
                                                text.text_run.get(),
                                                &text.range,
                                                baseline_origin,
                                                text.text_color);
                });
                let width = text.base.bounds.size.width;
                let underline_size = font_metrics.underline_size;
                let underline_offset = font_metrics.underline_offset;
                let strikeout_size = font_metrics.strikeout_size;
                let strikeout_offset = font_metrics.strikeout_offset;

                if text_run.decoration.underline || text.flags.override_underline() {
                    let underline_y = baseline_origin.y - underline_offset;
                    let underline_bounds = Rect(Point2D(baseline_origin.x, underline_y),
                                                Size2D(width, underline_size));
                    render_context.draw_solid_color(&underline_bounds, text.underline_color);
                }
                if text_run.decoration.overline || text.flags.override_overline() {
                    let overline_bounds = Rect(Point2D(baseline_origin.x, origin.y),
                                               Size2D(width, underline_size));
                    render_context.draw_solid_color(&overline_bounds, text.overline_color);
                }
                if text_run.decoration.line_through || text.flags.override_line_through() {
                    let strikeout_y = baseline_origin.y - strikeout_offset;
                    let strikeout_bounds = Rect(Point2D(baseline_origin.x, strikeout_y),
                                                Size2D(width, strikeout_size));
                    render_context.draw_solid_color(&strikeout_bounds, text.line_through_color);
                }
            }

            ImageDisplayItemClass(ref image_item) => {
                debug!("Drawing image at {:?}.", image_item.base.bounds);

                render_context.draw_image(image_item.base.bounds, image_item.image.clone())
            }

            BorderDisplayItemClass(ref border) => {
                render_context.draw_border(&border.base.bounds,
                                           border.border,
                                           border.color,
                                           border.style)
            }
        }
    }

    pub fn base<'a>(&'a self) -> &'a BaseDisplayItem<E> {
        // FIXME(tkuehn): Workaround for Rust region bug.
        unsafe {
            match *self {
                SolidColorDisplayItemClass(ref solid_color) => transmute_region(&solid_color.base),
                TextDisplayItemClass(ref text) => transmute_region(&text.base),
                ImageDisplayItemClass(ref image_item) => transmute_region(&image_item.base),
                BorderDisplayItemClass(ref border) => transmute_region(&border.base),
                ClipDisplayItemClass(ref clip) => transmute_region(&clip.base),
            }
        }
    }

    pub fn bounds(&self) -> Rect<Au> {
        self.base().bounds
    }

    pub fn children<'a>(&'a self) -> DisplayItemIterator<'a,E> {
        match *self {
            ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()),
            SolidColorDisplayItemClass(..) |
            TextDisplayItemClass(..) |
            ImageDisplayItemClass(..) |
            BorderDisplayItemClass(..) => EmptyDisplayItemIterator,
        }
    }

    pub fn debug_str(&self) -> ~str {
        let class = match *self {
            SolidColorDisplayItemClass(_) => "SolidColor",
            TextDisplayItemClass(_) => "Text",
            ImageDisplayItemClass(_) => "Image",
            BorderDisplayItemClass(_) => "Border",
            ClipDisplayItemClass(_) => "Clip",
        };
        let mut string = format!("{} @ {:?}", class, self.base().bounds);
        for child in self.children() {
            string = format!("{}\n  {}", string, child.debug_str());
        }
        string
    }
}

<commit_msg>auto merge of #1529 : ibnc/servo/display_list_debug, r=metajack<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/. */

//! Servo heavily uses display lists, which are retained-mode lists of rendering commands to
/// perform. Using a list instead of rendering elements in immediate mode allows transforms, hit
/// testing, and invalidation to be performed using the same primitives as painting. It also allows
/// Servo to aggressively cull invisible and out-of-bounds rendering elements, to reduce overdraw.
/// Finally, display lists allow tiles to be farmed out onto multiple CPUs and rendered in
/// parallel (although this benefit does not apply to GPU-based rendering).
///
/// Display items describe relatively high-level drawing operations (for example, entire borders
/// and shadows instead of lines and blur operations), to reduce the amount of allocation required.
/// They are therefore not exactly analogous to constructs like Skia pictures, which consist of
/// low-level drawing primitives.

use color::Color;
use render_context::RenderContext;
use text::TextRun;

use extra::arc::Arc;
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use servo_net::image::base::Image;
use servo_util::geometry::Au;
use servo_util::range::Range;
use std::cast::transmute_region;
use std::vec::VecIterator;
use style::computed_values::border_style;

/// A list of rendering operations to be performed.
pub struct DisplayList<E> {
    list: ~[DisplayItem<E>]
}

impl<E> DisplayList<E> {
    /// Creates a new display list.
    pub fn new() -> DisplayList<E> {
        DisplayList {
            list: ~[]
        }
    }

    fn dump(&self) {
        for item in self.list.iter() {
            item.debug_with_level(0);
        }
    }

    /// Appends the given item to the display list.
    pub fn append_item(&mut self, item: DisplayItem<E>) {
        // FIXME(Issue #150): crashes
        //debug!("Adding display item {:u}: {}", self.len(), item);
        self.list.push(item)
    }

    /// Draws the display list into the given render context.
    pub fn draw_into_context(&self, render_context: &mut RenderContext) {
        debug!("Beginning display list.");
        for item in self.list.iter() {
            // FIXME(Issue #150): crashes
            //debug!("drawing {}", *item);
            item.draw_into_context(render_context)
        }
        debug!("Ending display list.");
        debug!("{:?}", self.dump());
    }

    /// Returns a preorder iterator over the given display list.
    pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a,E> {
        ParentDisplayItemIterator(self.list.iter())
    }
}

/// One drawing command in the list.
pub enum DisplayItem<E> {
    SolidColorDisplayItemClass(~SolidColorDisplayItem<E>),
    TextDisplayItemClass(~TextDisplayItem<E>),
    ImageDisplayItemClass(~ImageDisplayItem<E>),
    BorderDisplayItemClass(~BorderDisplayItem<E>),
    ClipDisplayItemClass(~ClipDisplayItem<E>)
}

/// Information common to all display items.
pub struct BaseDisplayItem<E> {
    /// The boundaries of the display item.
    ///
    /// TODO: Which coordinate system should this use?
    bounds: Rect<Au>,

    /// Extra data: either the originating flow (for hit testing) or nothing (for rendering).
    extra: E,
}

/// Renders a solid color.
pub struct SolidColorDisplayItem<E> {
    base: BaseDisplayItem<E>,
    color: Color,
}

/// Renders text.
pub struct TextDisplayItem<E> {
    /// Fields common to all display items.
    base: BaseDisplayItem<E>,

    /// The text run.
    text_run: Arc<~TextRun>,

    /// The range of text within the text run.
    range: Range,

    /// The color of the text.
    text_color: Color,

    /// A bitfield of flags for text display items.
    flags: TextDisplayItemFlags,

    /// The color of text-decorations
    underline_color: Color,
    overline_color: Color,
    line_through_color: Color,
}

/// Flags for text display items.
pub struct TextDisplayItemFlags(u8);

impl TextDisplayItemFlags {
    pub fn new() -> TextDisplayItemFlags {
        TextDisplayItemFlags(0)
    }
}

// Whether underlining is forced on.
bitfield!(TextDisplayItemFlags, override_underline, set_override_underline, 0x01)
// Whether overlining is forced on.
bitfield!(TextDisplayItemFlags, override_overline, set_override_overline, 0x02)
// Whether line-through is forced on.
bitfield!(TextDisplayItemFlags, override_line_through, set_override_line_through, 0x04)

/// Renders an image.
pub struct ImageDisplayItem<E> {
    base: BaseDisplayItem<E>,
    image: Arc<~Image>,
}

/// Renders a border.
pub struct BorderDisplayItem<E> {
    base: BaseDisplayItem<E>,

    /// The border widths
    border: SideOffsets2D<Au>,

    /// The border colors.
    color: SideOffsets2D<Color>,

    /// The border styles.
    style: SideOffsets2D<border_style::T>
}

pub struct ClipDisplayItem<E> {
    base: BaseDisplayItem<E>,
    child_list: ~[DisplayItem<E>],
    need_clip: bool
}

pub enum DisplayItemIterator<'a,E> {
    EmptyDisplayItemIterator,
    ParentDisplayItemIterator(VecIterator<'a,DisplayItem<E>>),
}

impl<'a,E> Iterator<&'a DisplayItem<E>> for DisplayItemIterator<'a,E> {
    #[inline]
    fn next(&mut self) -> Option<&'a DisplayItem<E>> {
        match *self {
            EmptyDisplayItemIterator => None,
            ParentDisplayItemIterator(ref mut subiterator) => subiterator.next(),
        }
    }
}

impl<E> DisplayItem<E> {
    /// Renders this display item into the given render context.
    fn draw_into_context(&self, render_context: &mut RenderContext) {
        match *self {
            SolidColorDisplayItemClass(ref solid_color) => {
                render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
            }

            ClipDisplayItemClass(ref clip) => {
                if clip.need_clip {
                    render_context.draw_push_clip(&clip.base.bounds);
                }
                for item in clip.child_list.iter() {
                    (*item).draw_into_context(render_context);
                }
                if clip.need_clip {
                    render_context.draw_pop_clip();
                }
            }

            TextDisplayItemClass(ref text) => {
                debug!("Drawing text at {:?}.", text.base.bounds);

                // FIXME(pcwalton): Allocating? Why?
                let text_run = text.text_run.get();
                let font = render_context.font_ctx.get_font_by_descriptor(&text_run.font_descriptor).unwrap();

                let font_metrics = font.borrow().with(|font| {
                    font.metrics.clone()
                });
                let origin = text.base.bounds.origin;
                let baseline_origin = Point2D(origin.x, origin.y + font_metrics.ascent);
                font.borrow().with_mut(|font| {
                    font.draw_text_into_context(render_context,
                                                text.text_run.get(),
                                                &text.range,
                                                baseline_origin,
                                                text.text_color);
                });
                let width = text.base.bounds.size.width;
                let underline_size = font_metrics.underline_size;
                let underline_offset = font_metrics.underline_offset;
                let strikeout_size = font_metrics.strikeout_size;
                let strikeout_offset = font_metrics.strikeout_offset;

                if text_run.decoration.underline || text.flags.override_underline() {
                    let underline_y = baseline_origin.y - underline_offset;
                    let underline_bounds = Rect(Point2D(baseline_origin.x, underline_y),
                                                Size2D(width, underline_size));
                    render_context.draw_solid_color(&underline_bounds, text.underline_color);
                }
                if text_run.decoration.overline || text.flags.override_overline() {
                    let overline_bounds = Rect(Point2D(baseline_origin.x, origin.y),
                                               Size2D(width, underline_size));
                    render_context.draw_solid_color(&overline_bounds, text.overline_color);
                }
                if text_run.decoration.line_through || text.flags.override_line_through() {
                    let strikeout_y = baseline_origin.y - strikeout_offset;
                    let strikeout_bounds = Rect(Point2D(baseline_origin.x, strikeout_y),
                                                Size2D(width, strikeout_size));
                    render_context.draw_solid_color(&strikeout_bounds, text.line_through_color);
                }
            }

            ImageDisplayItemClass(ref image_item) => {
                debug!("Drawing image at {:?}.", image_item.base.bounds);

                render_context.draw_image(image_item.base.bounds, image_item.image.clone())
            }

            BorderDisplayItemClass(ref border) => {
                render_context.draw_border(&border.base.bounds,
                                           border.border,
                                           border.color,
                                           border.style)
            }
        }
    }

    pub fn base<'a>(&'a self) -> &'a BaseDisplayItem<E> {
        // FIXME(tkuehn): Workaround for Rust region bug.
        unsafe {
            match *self {
                SolidColorDisplayItemClass(ref solid_color) => transmute_region(&solid_color.base),
                TextDisplayItemClass(ref text) => transmute_region(&text.base),
                ImageDisplayItemClass(ref image_item) => transmute_region(&image_item.base),
                BorderDisplayItemClass(ref border) => transmute_region(&border.base),
                ClipDisplayItemClass(ref clip) => transmute_region(&clip.base),
            }
        }
    }

    pub fn bounds(&self) -> Rect<Au> {
        self.base().bounds
    }

    pub fn children<'a>(&'a self) -> DisplayItemIterator<'a,E> {
        match *self {
            ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()),
            SolidColorDisplayItemClass(..) |
            TextDisplayItemClass(..) |
            ImageDisplayItemClass(..) |
            BorderDisplayItemClass(..) => EmptyDisplayItemIterator,
        }
    }

    pub fn debug_with_level(&self, level: uint) {
            let mut indent = ~"";
            for _ in range(0, level) {
                indent.push_str("| ")
            }
            debug!("{}+ {}", indent, self.debug_str());
            for child in self.children() {
                child.debug_with_level(level + 1);
            }
    }

    pub fn debug_str(&self) -> ~str {
        let class = match *self {
            SolidColorDisplayItemClass(_) => "SolidColor",
            TextDisplayItemClass(_) => "Text",
            ImageDisplayItemClass(_) => "Image",
            BorderDisplayItemClass(_) => "Border",
            ClipDisplayItemClass(_) => "Clip",
        };
        format!("{} @ {:?}", class, self.base().bounds)
    }
}

<|endoftext|>


<commit_before>//! A macro for writing HTML templates.
//!
//! This crate, along with its sister `maud_macros`, lets you generate
//! HTML markup from within Rust. It exposes a single macro, `html!`,
//! which compiles your markup to specialized Rust code.
//!
//! # Dependencies
//!
//! To get started, add `maud` and `maud_macros` to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! # ...
//! maud = "*"
//! maud_macros = "*"
//! ```
//!
//! # Example
//!
//! ```rust
//! #![feature(plugin)]
//! #![allow(unstable)]
//!
//! extern crate maud;
//! #[plugin] #[no_link] extern crate maud_macros;
//!
//! fn main() {
//!     let name = "Lyra";
//!     let markup = html! {
//!         p { "Hi, " $name "!" }
//!     };
//!     assert_eq!(markup.render(), "<p>Hi, Lyra!</p>");
//! }
//! ```
//!
//! # Syntax
//!
//! Maud uses its own syntax, loosely inspired by HTML and Rust.
//!
//! ## Literals
//!
//! ```
//! html! {
//!     "Oatmeal, are you crazy?\n"
//!     "<script>alert(\"XSS\")</script>\n"
//!     $$"<script>alert(\"XSS\")</script>\n"
//! }
//! ```
//!
//! ```html
//! Oatmeal, are you crazy?
//! <script>alert("XSS")</script>
//! <script>alert("XSS")</script>
//! ```
//!
//! Literal strings use the same syntax as Rust.
//!
//! By default, HTML special characters are escaped automatically. Add a
//! `$$` prefix to disable this escaping.
//!
//! ## Elements
//!
//! ```
//! html! {
//!     h1 "Pinkie's Brew"
//!     p {
//!         "Watch as I work my gypsy magic"
//!         br /
//!         "Eye of a newt and cinnamon"
//!     }
//!     p small em "squee"
//! }
//! ```
//!
//! ```html
//! <h1>Pinkie's Brew</h1>
//! <p>
//!   Watch as I work my gypsy magic
//!   <br>
//!   Eye of a newt and cinnamon
//! </p>
//! <p><small><em>squee</em></small></p>
//! ```
//!
//! Write an element using curly braces (`p {}`).
//!
//! Terminate a void element using a slash (`br /`).
//!
//! If the element has only a single child, you can omit the brackets
//! (`h1 "Pinkie's Brew"`). This shorthand works with nested elements
//! too.
//!
//! ## Attributes
//!
//! ```
//! html! {
//!     form method="POST" {
//!         label for="waffles" "Do you like waffles?"
//!         input name="waffles" type="checkbox" checked=! /
//!     }
//! }
//! ```
//!
//! ```html
//! <form method="POST">
//!   <label for="waffles">Do you like waffles?</label>
//!   <input name="waffles" type="checkbox" checked>
//! </form>
//! ```
//!
//! Add attributes using the syntax `attr="value"`. Attributes must be
//! quoted: they are parsed as string literals.
//!
//! To declare an empty attribute, use `!` for the value: `checked=!`.
//!
//! ## Splices
//!
//! ```
//! let best_pony = "Pinkie Pie";
//! let numbers = [1, 2, 3, 4];
//! let secret_message = "Surprise!";
//! let pre_escaped = "<p>Pre-escaped</p>";
//! html! {
//!     h1 { $best_pony " says:" }
//!     p {
//!         "I have " $numbers.len() " numbers, "
//!         "and the first one is " $numbers[0]
//!     }
//!     p title=$secret_message {
//!         "1 + 1 = " $(1 + 1)
//!     }
//!     $$pre_escaped
//! }
//! ```
//!
//! ```html
//! <h1>Pinkie Pie says:</h1>
//! <p>I have 4 numbers, and the first one is 1</p>
//! <p title="Surprise!">1 + 1 = 2</p>
//! <p>Pre-escaped</p>
//! ```
//!
//! Use `$(expr)` syntax to splice a Rust expression into the output.
//! You can omit the brackets if it's just a variable (`$var`), indexing
//! operation (`$var[0]`), method call (`$var.method()`), or property
//! lookup (`$var.property`).
//!
//! As with literals, expression values are escaped by default. Use a
//! `$$` prefix to disable this behavior.

#![allow(unstable)]

use std::fmt;
use std::io::{IoError, IoErrorKind, IoResult};

/// Escape an HTML value.
pub fn escape(s: &str) -> String {
    use std::fmt::Writer;
    let mut buf = String::new();
    rt::Escaper { inner: &mut buf }.write_str(s).unwrap();
    buf
}

/// A block of HTML markup, as returned by the `html!` macro.
///
/// Use `.render()` to convert it to a `String`, or `.render_to()` to
/// write it directly to a handle.
pub struct Markup<'a> {
    callback: &'a (Fn(&mut fmt::Writer) -> fmt::Result + 'a),
}

impl<'a> Markup<'a> {
    /// Render the markup to a `String`.
    pub fn render(&self) -> String {
        let mut buf = String::new();
        self.render_fmt(&mut buf).unwrap();
        buf
    }

    /// Render the markup to a `std::io::Writer`.
    ///
    /// If you're writing to, say, a file or a socket, this may be more
    /// efficient than calling `.render()`.
    pub fn render_to(&self, w: &mut Writer) -> IoResult<()> {
        struct WriterWrapper<'a, 'b: 'a> {
            inner: &'a mut (Writer + 'b),
        }
        impl<'a, 'b> fmt::Writer for WriterWrapper<'a, 'b> {
            fn write_str(&mut self, s: &str) -> fmt::Result {
                self.inner.write_str(s).map_err(|_| fmt::Error)
            }
        }
        self.render_fmt(&mut WriterWrapper { inner: w })
            .map_err(|_| IoError {
                kind: IoErrorKind::OtherIoError,
                desc: "formatting error",
                detail: None,
            })
    }

    /// Render the markup to a `std::fmt::Writer`.
    pub fn render_fmt(&self, w: &mut fmt::Writer) -> fmt::Result {
        (self.callback)(w)
    }
}

/// Internal functions used by the `maud_macros` package. You should
/// never need to call these directly.
#[doc(hidden)]
pub mod rt {
    use std::fmt;
    use super::Markup;

    #[inline]
    pub fn make_markup<'a>(f: &'a (Fn(&mut fmt::Writer) -> fmt::Result + 'a)) -> Markup<'a> {
        Markup { callback: f }
    }

    /// rustc is a butt and doesn't let us quote macro invocations
    /// directly. So we factor the `write!` call into a separate
    /// function and use that instead.
    ///
    /// See <https://github.com/rust-lang/rust/issues/16617>
    #[inline]
    pub fn write_fmt<T: fmt::Display>(w: &mut fmt::Writer, value: T) -> fmt::Result {
        write!(w, "{}", value)
    }

    pub struct Escaper<'a, 'b: 'a> {
        pub inner: &'a mut (fmt::Writer + 'b),
    }

    impl<'a, 'b> fmt::Writer for Escaper<'a, 'b> {
        fn write_str(&mut self, s: &str) -> fmt::Result {
            for c in s.chars() {
                try!(match c {
                    '&' => self.inner.write_str("&"),
                    '<' => self.inner.write_str("<"),
                    '>' => self.inner.write_str(">"),
                    '"' => self.inner.write_str("""),
                    '\'' => self.inner.write_str("'"),
                    _ => write!(self.inner, "{}", c),
                });
            }
            Ok(())
        }
    }
}
<commit_msg>Add some more docs<commit_after>//! A macro for writing HTML templates.
//!
//! This crate, along with its sister `maud_macros`, lets you generate
//! HTML markup from within Rust. It exposes a single macro, `html!`,
//! which compiles your markup to specialized Rust code.
//!
//! # Dependencies
//!
//! To get started, add `maud` and `maud_macros` to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! # ...
//! maud = "*"
//! maud_macros = "*"
//! ```
//!
//! # Example
//!
//! ```rust
//! #![feature(plugin)]
//! #![allow(unstable)]
//!
//! extern crate maud;
//! #[plugin] #[no_link] extern crate maud_macros;
//!
//! fn main() {
//!     let name = "Lyra";
//!     let markup = html! {
//!         p { "Hi, " $name "!" }
//!     };
//!     assert_eq!(markup.render(), "<p>Hi, Lyra!</p>");
//! }
//! ```
//!
//! # Syntax
//!
//! Maud uses its own syntax, loosely inspired by HTML and Rust.
//!
//! **Note:** The markup you see below has been cleaned up a bit. In
//! reality, Maud doesn't add extra whitespace to the HTML it generates.
//!
//! ## Literals
//!
//! ```
//! html! {
//!     "Oatmeal, are you crazy?\n"
//!     "<script>alert(\"XSS\")</script>\n"
//!     $$"<script>alert(\"XSS\")</script>\n"
//! }
//! ```
//!
//! ```html
//! Oatmeal, are you crazy?
//! <script>alert("XSS")</script>
//! <script>alert("XSS")</script>
//! ```
//!
//! Literal strings use the same syntax as Rust. Wrap them in
//! double quotes, and use a backslash for escapes.
//!
//! By default, HTML special characters are escaped automatically. Add a
//! `$$` prefix to disable this escaping.
//!
//! ## Elements
//!
//! ```
//! html! {
//!     h1 "Pinkie's Brew"
//!     p {
//!         "Watch as I work my gypsy magic"
//!         br /
//!         "Eye of a newt and cinnamon"
//!     }
//!     p small em "squee"
//! }
//! ```
//!
//! ```html
//! <h1>Pinkie's Brew</h1>
//! <p>
//!   Watch as I work my gypsy magic
//!   <br>
//!   Eye of a newt and cinnamon
//! </p>
//! <p><small><em>squee</em></small></p>
//! ```
//!
//! Write an element using curly braces (`p {}`).
//!
//! Terminate a void element using a slash (`br /`).
//!
//! If the element has only a single child, you can omit the brackets
//! (`h1 "Pinkie's Brew"`). This shorthand works with nested elements
//! too.
//!
//! ## Attributes
//!
//! ```
//! html! {
//!     form method="POST" {
//!         label for="waffles" "Do you like waffles?"
//!         input name="waffles" type="checkbox" checked=! /
//!     }
//! }
//! ```
//!
//! ```html
//! <form method="POST">
//!   <label for="waffles">Do you like waffles?</label>
//!   <input name="waffles" type="checkbox" checked>
//! </form>
//! ```
//!
//! Add attributes using the syntax `attr="value"`. Attributes must be
//! quoted: they are parsed as string literals.
//!
//! To declare an empty attribute, use `!` for the value: `checked=!`.
//!
//! ## Splices
//!
//! ```
//! let best_pony = "Pinkie Pie";
//! let numbers = [1, 2, 3, 4];
//! let secret_message = "Surprise!";
//! let pre_escaped = "<p>Pre-escaped</p>";
//! html! {
//!     h1 { $best_pony " says:" }
//!     p {
//!         "I have " $numbers.len() " numbers, "
//!         "and the first one is " $numbers[0]
//!     }
//!     p title=$secret_message {
//!         "1 + 1 = " $(1 + 1)
//!     }
//!     $$pre_escaped
//! }
//! ```
//!
//! ```html
//! <h1>Pinkie Pie says:</h1>
//! <p>I have 4 numbers, and the first one is 1</p>
//! <p title="Surprise!">1 + 1 = 2</p>
//! <p>Pre-escaped</p>
//! ```
//!
//! Use `$(expr)` syntax to splice a Rust expression into the output.
//! You can omit the brackets if it's just a variable (`$var`), indexing
//! operation (`$var[0]`), method call (`$var.method()`), or property
//! lookup (`$var.property`).
//!
//! As with literals, expression values are escaped by default. Use a
//! `$$` prefix to disable this behavior.

#![allow(unstable)]

use std::fmt;
use std::io::{IoError, IoErrorKind, IoResult};

/// Escape an HTML value.
pub fn escape(s: &str) -> String {
    use std::fmt::Writer;
    let mut buf = String::new();
    rt::Escaper { inner: &mut buf }.write_str(s).unwrap();
    buf
}

/// A block of HTML markup, as returned by the `html!` macro.
///
/// Use `.render()` to convert it to a `String`, or `.render_to()` to
/// write it directly to a handle.
pub struct Markup<'a> {
    callback: &'a (Fn(&mut fmt::Writer) -> fmt::Result + 'a),
}

impl<'a> Markup<'a> {
    /// Render the markup to a `String`.
    pub fn render(&self) -> String {
        let mut buf = String::new();
        self.render_fmt(&mut buf).unwrap();
        buf
    }

    /// Render the markup to a `std::io::Writer`.
    ///
    /// If you're writing to, say, a file or a socket, this may be more
    /// efficient than calling `.render()`.
    pub fn render_to(&self, w: &mut Writer) -> IoResult<()> {
        struct WriterWrapper<'a, 'b: 'a> {
            inner: &'a mut (Writer + 'b),
        }
        impl<'a, 'b> fmt::Writer for WriterWrapper<'a, 'b> {
            fn write_str(&mut self, s: &str) -> fmt::Result {
                self.inner.write_str(s).map_err(|_| fmt::Error)
            }
        }
        self.render_fmt(&mut WriterWrapper { inner: w })
            .map_err(|_| IoError {
                kind: IoErrorKind::OtherIoError,
                desc: "formatting error",
                detail: None,
            })
    }

    /// Render the markup to a `std::fmt::Writer`.
    pub fn render_fmt(&self, w: &mut fmt::Writer) -> fmt::Result {
        (self.callback)(w)
    }
}

/// Internal functions used by the `maud_macros` package. You should
/// never need to call these directly.
#[doc(hidden)]
pub mod rt {
    use std::fmt;
    use super::Markup;

    #[inline]
    pub fn make_markup<'a>(f: &'a (Fn(&mut fmt::Writer) -> fmt::Result + 'a)) -> Markup<'a> {
        Markup { callback: f }
    }

    /// rustc is a butt and doesn't let us quote macro invocations
    /// directly. So we factor the `write!` call into a separate
    /// function and use that instead.
    ///
    /// See <https://github.com/rust-lang/rust/issues/16617>
    #[inline]
    pub fn write_fmt<T: fmt::Display>(w: &mut fmt::Writer, value: T) -> fmt::Result {
        write!(w, "{}", value)
    }

    pub struct Escaper<'a, 'b: 'a> {
        pub inner: &'a mut (fmt::Writer + 'b),
    }

    impl<'a, 'b> fmt::Writer for Escaper<'a, 'b> {
        fn write_str(&mut self, s: &str) -> fmt::Result {
            for c in s.chars() {
                try!(match c {
                    '&' => self.inner.write_str("&"),
                    '<' => self.inner.write_str("<"),
                    '>' => self.inner.write_str(">"),
                    '"' => self.inner.write_str("""),
                    '\'' => self.inner.write_str("'"),
                    _ => write!(self.inner, "{}", c),
                });
            }
            Ok(())
        }
    }
}
<|endoftext|>


<commit_before>use rustc::mir::visit::Visitor;
use rustc::mir::{self, Local, Location};
use rustc::ty::{self, TyCtxt};
use rustc_index::bit_set::BitSet;
use syntax_pos::DUMMY_SP;

use crate::dataflow::{self, GenKillSet};

/// Whether a borrow to a `Local` has been created that could allow that `Local` to be mutated
/// indirectly. This could either be a mutable reference (`&mut`) or a shared borrow if the type of
/// that `Local` allows interior mutability. Operations that can mutate local's indirectly include:
/// assignments through a pointer (`*p = 42`), function calls, drop terminators and inline assembly.
///
/// If this returns false for a `Local` at a given statement (or terminator), that `Local` could
/// not possibly have been mutated indirectly prior to that statement.
#[derive(Copy, Clone)]
pub struct IndirectlyMutableLocals<'mir, 'tcx> {
    body: &'mir mir::Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
}

impl<'mir, 'tcx> IndirectlyMutableLocals<'mir, 'tcx> {
    pub fn new(
        tcx: TyCtxt<'tcx>,
        body: &'mir mir::Body<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> Self {
        IndirectlyMutableLocals { body, tcx, param_env }
    }

    fn transfer_function<'a>(
        &self,
        trans: &'a mut GenKillSet<Local>,
    ) -> TransferFunction<'a, 'mir, 'tcx> {
        TransferFunction {
            body: self.body,
            tcx: self.tcx,
            param_env: self.param_env,
            trans
        }
    }
}

impl<'mir, 'tcx> dataflow::BitDenotation<'tcx> for IndirectlyMutableLocals<'mir, 'tcx> {
    type Idx = Local;

    fn name() -> &'static str { "mut_borrowed_locals" }

    fn bits_per_block(&self) -> usize {
        self.body.local_decls.len()
    }

    fn start_block_effect(&self, _entry_set: &mut BitSet<Local>) {
        // Nothing is borrowed on function entry
    }

    fn statement_effect(
        &self,
        trans: &mut GenKillSet<Local>,
        loc: Location,
    ) {
        let stmt = &self.body[loc.block].statements[loc.statement_index];
        self.transfer_function(trans).visit_statement(stmt, loc);
    }

    fn terminator_effect(
        &self,
        trans: &mut GenKillSet<Local>,
        loc: Location,
    ) {
        let terminator = self.body[loc.block].terminator();
        self.transfer_function(trans).visit_terminator(terminator, loc);
    }

    fn propagate_call_return(
        &self,
        _in_out: &mut BitSet<Local>,
        _call_bb: mir::BasicBlock,
        _dest_bb: mir::BasicBlock,
        _dest_place: &mir::Place<'tcx>,
    ) {
        // Nothing to do when a call returns successfully
    }
}

impl<'mir, 'tcx> dataflow::BottomValue for IndirectlyMutableLocals<'mir, 'tcx> {
    // bottom = unborrowed
    const BOTTOM_VALUE: bool = false;
}

/// A `Visitor` that defines the transfer function for `IndirectlyMutableLocals`.
struct TransferFunction<'a, 'mir, 'tcx> {
    trans: &'a mut GenKillSet<Local>,
    body: &'mir mir::Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
}

impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
    fn visit_rvalue(
        &mut self,
        rvalue: &mir::Rvalue<'tcx>,
        location: Location,
    ) {
        if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
            let is_mut = match kind {
                mir::BorrowKind::Mut { .. } => true,

                | mir::BorrowKind::Shared
                | mir::BorrowKind::Shallow
                | mir::BorrowKind::Unique
                => {
                    !borrowed_place
                        .ty(self.body, self.tcx)
                        .ty
                        .is_freeze(self.tcx, self.param_env, DUMMY_SP)
                }
            };

            if is_mut {
                match borrowed_place.base {
                    mir::PlaceBase::Local(borrowed_local) if !borrowed_place.is_indirect()
                        => self.trans.gen(borrowed_local),

                    _ => (),
                }
            }
        }

        self.super_rvalue(rvalue, location);
    }

    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
        // This method purposely does nothing except call `super_terminator`. It exists solely to
        // document the subtleties around drop terminators.

        self.super_terminator(terminator, location);

        if let mir::TerminatorKind::Drop { location: _, .. }
             | mir::TerminatorKind::DropAndReplace { location: _, .. } = &terminator.kind
        {
            // Although drop terminators mutably borrow the location being dropped, that borrow
            // cannot live beyond the drop terminator because the dropped location is invalidated.
        }
    }
}
<commit_msg>Don't mark zero-sized arrays as indirectly mutable when borrowed<commit_after>use rustc::mir::visit::Visitor;
use rustc::mir::{self, Local, Location};
use rustc::ty::{self, TyCtxt};
use rustc_index::bit_set::BitSet;
use syntax_pos::DUMMY_SP;

use crate::dataflow::{self, GenKillSet};

/// Whether a borrow to a `Local` has been created that could allow that `Local` to be mutated
/// indirectly. This could either be a mutable reference (`&mut`) or a shared borrow if the type of
/// that `Local` allows interior mutability. Operations that can mutate local's indirectly include:
/// assignments through a pointer (`*p = 42`), function calls, drop terminators and inline assembly.
///
/// If this returns false for a `Local` at a given statement (or terminator), that `Local` could
/// not possibly have been mutated indirectly prior to that statement.
#[derive(Copy, Clone)]
pub struct IndirectlyMutableLocals<'mir, 'tcx> {
    body: &'mir mir::Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
}

impl<'mir, 'tcx> IndirectlyMutableLocals<'mir, 'tcx> {
    pub fn new(
        tcx: TyCtxt<'tcx>,
        body: &'mir mir::Body<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> Self {
        IndirectlyMutableLocals { body, tcx, param_env }
    }

    fn transfer_function<'a>(
        &self,
        trans: &'a mut GenKillSet<Local>,
    ) -> TransferFunction<'a, 'mir, 'tcx> {
        TransferFunction {
            body: self.body,
            tcx: self.tcx,
            param_env: self.param_env,
            trans
        }
    }
}

impl<'mir, 'tcx> dataflow::BitDenotation<'tcx> for IndirectlyMutableLocals<'mir, 'tcx> {
    type Idx = Local;

    fn name() -> &'static str { "mut_borrowed_locals" }

    fn bits_per_block(&self) -> usize {
        self.body.local_decls.len()
    }

    fn start_block_effect(&self, _entry_set: &mut BitSet<Local>) {
        // Nothing is borrowed on function entry
    }

    fn statement_effect(
        &self,
        trans: &mut GenKillSet<Local>,
        loc: Location,
    ) {
        let stmt = &self.body[loc.block].statements[loc.statement_index];
        self.transfer_function(trans).visit_statement(stmt, loc);
    }

    fn terminator_effect(
        &self,
        trans: &mut GenKillSet<Local>,
        loc: Location,
    ) {
        let terminator = self.body[loc.block].terminator();
        self.transfer_function(trans).visit_terminator(terminator, loc);
    }

    fn propagate_call_return(
        &self,
        _in_out: &mut BitSet<Local>,
        _call_bb: mir::BasicBlock,
        _dest_bb: mir::BasicBlock,
        _dest_place: &mir::Place<'tcx>,
    ) {
        // Nothing to do when a call returns successfully
    }
}

impl<'mir, 'tcx> dataflow::BottomValue for IndirectlyMutableLocals<'mir, 'tcx> {
    // bottom = unborrowed
    const BOTTOM_VALUE: bool = false;
}

/// A `Visitor` that defines the transfer function for `IndirectlyMutableLocals`.
struct TransferFunction<'a, 'mir, 'tcx> {
    trans: &'a mut GenKillSet<Local>,
    body: &'mir mir::Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
}

impl<'tcx> TransferFunction<'_, '_, 'tcx> {
    /// Returns `true` if this borrow would allow mutation of the `borrowed_place`.
    fn borrow_allows_mutation(
        &self,
        kind: mir::BorrowKind,
        borrowed_place: &mir::Place<'tcx>,
    ) -> bool {
        let borrowed_ty = borrowed_place.ty(self.body, self.tcx).ty;

        // Zero-sized types cannot be mutated, since there is nothing inside to mutate.
        //
        // FIXME: For now, we only exempt arrays of length zero. We need to carefully
        // consider the effects before extending this to all ZSTs.
        if let ty::Array(_, len) = borrowed_ty.kind {
            if len.try_eval_usize(self.tcx, self.param_env) == Some(0) {
                return false;
            }
        }

        match kind {
            mir::BorrowKind::Mut { .. } => true,

            | mir::BorrowKind::Shared
            | mir::BorrowKind::Shallow
            | mir::BorrowKind::Unique
            => !borrowed_ty.is_freeze(self.tcx, self.param_env, DUMMY_SP),
        }
    }
}

impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
    fn visit_rvalue(
        &mut self,
        rvalue: &mir::Rvalue<'tcx>,
        location: Location,
    ) {
        if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
            if self.borrow_allows_mutation(kind, borrowed_place) {
                match borrowed_place.base {
                    mir::PlaceBase::Local(borrowed_local) if !borrowed_place.is_indirect()
                        => self.trans.gen(borrowed_local),

                    _ => (),
                }
            }
        }

        self.super_rvalue(rvalue, location);
    }

    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
        // This method purposely does nothing except call `super_terminator`. It exists solely to
        // document the subtleties around drop terminators.

        self.super_terminator(terminator, location);

        if let mir::TerminatorKind::Drop { location: _, .. }
             | mir::TerminatorKind::DropAndReplace { location: _, .. } = &terminator.kind
        {
            // Although drop terminators mutably borrow the location being dropped, that borrow
            // cannot live beyond the drop terminator because the dropped location is invalidated.
        }
    }
}
<|endoftext|>


<commit_before>#![cfg(feature = "encoding")]

#![feature(test)]
extern crate test;

#[macro_use]
extern crate lambda_calculus as lambda;

use test::Bencher;
use lambda::data::numerals::{church, scott, parigot, stumpfu};
use lambda::*;

macro_rules! bench_num {
    ($encoding:ident, $name:ident, $conversion:ident, $function:ident, $order:expr, $($n:expr),+) => (
        #[bench]
        fn $name(b: &mut Bencher) {
            b.iter(|| { beta(app!($encoding::$function(), $($n.$conversion()),*), $order, 0) } );
        }
    );
}

bench_num!(church,  church_succ,  into_church,  succ, HAP, 1);
bench_num!(scott,   scott_succ,   into_scott,   succ, HAP, 1);
bench_num!(parigot, parigot_succ, into_parigot, succ, HAP, 1);
bench_num!(stumpfu, stumpfu_succ, into_stumpfu, succ, HAP, 1);

bench_num!(church,  church_pred,  into_church,  pred, HAP, 1);
bench_num!(scott,   scott_pred,   into_scott,   pred, HAP, 1);
bench_num!(parigot, parigot_pred, into_parigot, pred, HAP, 1);
bench_num!(stumpfu, stumpfu_pred, into_stumpfu, pred, HAP, 1);

bench_num!(church,  church_add,  into_church,  add, HAP, 2, 2);

bench_num!(parigot, parigot_add, into_parigot, add, HAP, 2, 2);
bench_num!(stumpfu, stumpfu_add, into_stumpfu, add, HAP, 2, 2);

bench_num!(church,  church_sub,  into_church,  sub, HAP, 2, 2);

bench_num!(parigot, parigot_sub, into_parigot, sub, HAP, 2, 2);

bench_num!(church,  church_mult,  into_church,  mult, HAP, 2, 2);

bench_num!(parigot, parigot_mult, into_parigot, mult, HAP, 2, 2);

bench_num!(church, church_pow,  into_church, pow,  HAP, 2, 2);
bench_num!(church, church_div,  into_church, div,  HAP, 2, 2);
bench_num!(church, church_quot, into_church, quot, HAP, 2, 2);
bench_num!(church, church_rem,  into_church, rem,  HAP, 2, 2);
bench_num!(church, church_eq,   into_church, eq,   HAP, 2, 2);
bench_num!(church, church_neq,  into_church, neq,  HAP, 2, 2);
bench_num!(church, church_gt,   into_church, gt,   HAP, 2, 2);
bench_num!(church, church_lt,   into_church, lt,   HAP, 2, 2);
bench_num!(church, church_geq,  into_church, geq,  HAP, 2, 2);
bench_num!(church, church_leq,  into_church, leq,  HAP, 2, 2);
bench_num!(church, church_min,  into_church, min,  HAP, 2, 2);
bench_num!(church, church_max,  into_church, max,  HAP, 2, 2);
bench_num!(church, church_lshift, into_church, lshift,  HAP, 2, 2);
bench_num!(church, church_rshift, into_church, rshift,  HAP, 2, 2);
bench_num!(church, church_fac,   into_church, fac, HAP, 3);
<commit_msg>fill in the blanks in benches<commit_after>#![cfg(feature = "encoding")]

#![feature(test)]
extern crate test;

#[macro_use]
extern crate lambda_calculus as lambda;

use test::Bencher;
use lambda::data::numerals::{church, scott, parigot, stumpfu};
use lambda::*;

macro_rules! bench_num {
    ($encoding:ident, $name:ident, $conversion:ident, $function:ident, $order:expr, $($n:expr),+) => (
        #[bench]
        fn $name(b: &mut Bencher) {
            b.iter(|| { beta(app!($encoding::$function(), $($n.$conversion()),*), $order, 0) } );
        }
    );
}

bench_num!(church,  church_succ,  into_church,  succ, HAP, 1);
bench_num!(scott,   scott_succ,   into_scott,   succ, HAP, 1);
bench_num!(parigot, parigot_succ, into_parigot, succ, HAP, 1);
bench_num!(stumpfu, stumpfu_succ, into_stumpfu, succ, HAP, 1);

bench_num!(church,  church_pred,  into_church,  pred, HAP, 1);
bench_num!(scott,   scott_pred,   into_scott,   pred, HAP, 1);
bench_num!(parigot, parigot_pred, into_parigot, pred, HAP, 1);
bench_num!(stumpfu, stumpfu_pred, into_stumpfu, pred, HAP, 1);

bench_num!(church,  church_add,  into_church,  add, HAP, 2, 2);
//bench_num!(scott,   scott_add,   into_scott,  add, HAP, 2, 2);
bench_num!(parigot, parigot_add, into_parigot, add, HAP, 2, 2);
bench_num!(stumpfu, stumpfu_add, into_stumpfu, add, HAP, 2, 2);

bench_num!(church,  church_sub,  into_church,  sub, HAP, 2, 2);
//bench_num!(scott,   scott_sub,   into_scott,  sub, HAP, 2, 2);
bench_num!(parigot, parigot_sub, into_parigot, sub, HAP, 2, 2);
//bench_num!(stumpfu, stumpfu_sub, into_stumpfu, sub, HAP, 2, 2);

bench_num!(church,  church_mult,  into_church,  mult, HAP, 2, 2);
//bench_num!(scott,   scott_mult,   into_scott,   mult, HAP, 2, 2);
bench_num!(parigot, parigot_mult, into_parigot, mult, HAP, 2, 2);
//bench_num!(stumpfu, stumpfu_mult, into_stumpfu, mult, HAP, 2, 2);

bench_num!(church, church_pow,  into_church, pow,  HAP, 2, 2);
bench_num!(church, church_div,  into_church, div,  HAP, 2, 2);
bench_num!(church, church_quot, into_church, quot, HAP, 2, 2);
bench_num!(church, church_rem,  into_church, rem,  HAP, 2, 2);
bench_num!(church, church_eq,   into_church, eq,   HAP, 2, 2);
bench_num!(church, church_neq,  into_church, neq,  HAP, 2, 2);
bench_num!(church, church_gt,   into_church, gt,   HAP, 2, 2);
bench_num!(church, church_lt,   into_church, lt,   HAP, 2, 2);
bench_num!(church, church_geq,  into_church, geq,  HAP, 2, 2);
bench_num!(church, church_leq,  into_church, leq,  HAP, 2, 2);
bench_num!(church, church_min,  into_church, min,  HAP, 2, 2);
bench_num!(church, church_max,  into_church, max,  HAP, 2, 2);
bench_num!(church, church_lshift, into_church, lshift,  HAP, 2, 2);
bench_num!(church, church_rshift, into_church, rshift,  HAP, 2, 2);
bench_num!(church, church_fac,   into_church, fac, HAP, 3);
<|endoftext|>


<commit_before>
#![feature(box_syntax,box_patterns)]
extern crate seax_svm;

use seax_svm::svm::slist::Stack;
use seax_svm::svm::slist::List::{Cons,Nil};
use seax_svm::svm::cell::Atom::*;
use seax_svm::svm::cell::SVMCell::*;
use seax_svm::svm::Inst::*;

macro_rules! list(
    ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
    ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
    () => ( Box::new(Nil) );
);

/// SVM integration tests.
///
/// These are based on the sample programs in Zach Allaun's Clojure SECD
/// [implementation](https://github.com/zachallaun/secd). Each example also
/// provides the source code for the equivalent Lisp program.

/// Test for simple list construction through CONS.
///
/// ```lisp
/// (cons 10 (cons 20 nil))
/// ```
#[test]
fn test_list_creation() {
    let state = seax_svm::svm::eval_program(list!(
        InstCell(NIL),
        InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
        InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS)
        ));
    assert_eq!(state.peek(), Some(&ListCell( box list!(AtomCell(SInt(20)), AtomCell(SInt(10))))));
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (car (cons 10 (cons 20 nil)))
/// ```
#[test]
fn test_list_car() {
    let state = seax_svm::svm::eval_program(list!(
        InstCell(NIL),
        InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
        InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
        InstCell(CAR)
        ));
    assert_eq!(state.peek(), Some(&AtomCell(SInt(20))));
}
/// Test for simple list construction and destructuring
///
/// ```lisp
/// (cdr (cons 10 (cons 20 nil)))
/// ```
#[test]
fn test_list_cdr() {
    assert_eq!(
        seax_svm::svm::eval_program(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CDR)
            )
        ).peek(),
        Some(&ListCell(box list!(AtomCell(SInt(10)))))
    );
}

/// Test for simple mathematics application
///
/// ```lisp
/// (+ 10 10)
/// ```
#[test]
fn test_simple_add() {
    let state = seax_svm::svm::eval_program(list!(
        InstCell(LDC), AtomCell(SInt(10)),
        InstCell(LDC), AtomCell(SInt(10)),
        InstCell(ADD)
        ));
    assert_eq!(state.peek(), Some(&AtomCell(SInt(20))));
}

/// Test for nested arithmetic
///
/// ```lisp
/// (- 20 (+ 5 5))
/// ```
#[test]
fn test_nested_arith() {
    let state = seax_svm::svm::eval_program(list!(
        InstCell(LDC), AtomCell(SInt(5)),
        InstCell(LDC), AtomCell(SInt(5)),
        InstCell(ADD),
        InstCell(LDC), AtomCell(SInt(20)),
        InstCell(SUB)
        ));
    assert_eq!(state.peek(), Some(&AtomCell(SInt(10))));
}

/// Test for basic branching
///
/// ```lisp
/// ((if (= 0 (- 1 1)) true false)
/// ```
#[test]
fn test_basic_branching() {
    let state = seax_svm::svm::eval_program(list!(
        InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)),
        InstCell(SUB),
        InstCell(LDC), AtomCell(SInt(0)),
        InstCell(EQ),
        InstCell(SEL),
            ListCell(box list!(InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN))),
            ListCell(box list!(InstCell(NIL), InstCell(JOIN)))
        ));
    assert_eq!(state.peek(), Some(&AtomCell(SInt(1))));
}
<commit_msg>Make all integration tests functional<commit_after>
#![feature(box_syntax,box_patterns)]
extern crate seax_svm;

use seax_svm::svm::slist::Stack;
use seax_svm::svm::slist::List::{Cons,Nil};
use seax_svm::svm::cell::Atom::*;
use seax_svm::svm::cell::SVMCell::*;
use seax_svm::svm::Inst::*;

macro_rules! list(
    ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
    ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
    () => ( Box::new(Nil) );
);

/// SVM integration tests.
///
/// These are based on the sample programs in Zach Allaun's Clojure SECD
/// [implementation](https://github.com/zachallaun/secd). Each example also
/// provides the source code for the equivalent Lisp program.

/// Test for simple list construction through CONS.
///
/// ```lisp
/// (cons 10 (cons 20 nil))
/// ```
#[test]
fn test_list_creation() {
    assert_eq!(
        seax_svm::svm::eval_program(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS)
        )).peek(),
        Some(&ListCell( box list!(AtomCell(SInt(20)), AtomCell(SInt(10)))))
    );
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (car (cons 10 (cons 20 nil)))
/// ```
#[test]
fn test_list_car() {
    assert_eq!(
        seax_svm::svm::eval_program(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CAR)
        )).peek(),
        Some(&AtomCell(SInt(20)))
    );
}
/// Test for simple list construction and destructuring
///
/// ```lisp
/// (cdr (cons 10 (cons 20 nil)))
/// ```
#[test]
fn test_list_cdr() {
    assert_eq!(
        seax_svm::svm::eval_program(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CDR)
        )).peek(),
        Some(&ListCell(box list!(AtomCell(SInt(10)))))
    );
}

/// Test for simple mathematics application
///
/// ```lisp
/// (+ 10 10)
/// ```
#[test]
fn test_simple_add() {
    assert_eq!(
        seax_svm::svm::eval_program(list!(
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(ADD)
        )).peek(),
        Some(&AtomCell(SInt(20)))
    );
}

/// Test for nested arithmetic
///
/// ```lisp
/// (- 20 (+ 5 5))
/// ```
#[test]
fn test_nested_arith() {
     assert_eq!(
        seax_svm::svm::eval_program(list!(
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(ADD),
            InstCell(LDC), AtomCell(SInt(20)),
            InstCell(SUB)
        )).peek(),
        Some(&AtomCell(SInt(10)))
    );
}

/// Test for basic branching
///
/// ```lisp
/// ((if (= 0 (- 1 1)) true false)
/// ```
#[test]
fn test_basic_branching() {
    assert_eq!(
        seax_svm::svm::eval_program(list!(
            InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(0)),
            InstCell(EQ),
            InstCell(SEL),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN))),
                ListCell(box list!(InstCell(NIL), InstCell(JOIN)))
        )).peek(),
        Some(&AtomCell(SInt(1)))
    );
}

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::fs::{File, metadata};
use std::ffi::OsStr;
use std::hash::{Hash, Hasher};
use std::io;
use std::path::{Path, PathBuf, Component};
use std::sync::{Arc, RwLock};
use std::str::from_utf8;

use futures_cpupool;
use futures::{Future};
use futures::future::{ok};
use mime_guess::guess_mime_type;
use mime::{TopLevel, Mime};
use tk_http::server::Error;
use tk_http::Status;
use tk_sendfile::{DiskPool, FileOpener, IntoFileOpener, FileReader};
use self_meter_http::Meter;

use config;
use config::static_files::{Static, Mode, SingleFile};
use default_error_page::{serve_error_page, error_page};
use incoming::{Input, Request, Reply, Transport};
use incoming::reply;
use intern::{DiskPoolName};
use runtime::Runtime;

quick_error! {
    #[derive(Debug)]
    enum FileError {
        Sendfile(err: io::Error) {
            description("sendfile error")
            cause(err)
        }
    }
}

struct PathOpen {
    path: PathBuf,
    settings: Arc<Static>,
    file: Option<(File, u64, Mime)>,
}

#[derive(Clone)]
pub struct DiskPools(Arc<RwLock<PoolsInternal>>);

struct PoolsInternal {
    pools: HashMap<DiskPoolName, (u64, DiskPool)>,
    default: DiskPool,
    meter: Meter,
}

pub fn serve_dir<S: Transport>(settings: &Arc<Static>, mut inp: Input)
    -> Request<S>
{
    // TODO(tailhook) check for symlink attacks
    let path = match path(settings, &inp) {
        Ok(p) => p,
        Err(()) => {
            return serve_error_page(Status::Forbidden, inp);
        }
    };
    inp.debug.set_fs_path(&path);
    let pool = get_pool(&inp.runtime, &settings.pool);
    let settings = settings.clone();
    reply(inp, move |mut e| {
        Box::new(pool.open(PathOpen::new(path, &settings))
            .then(move |res| match res {
                Ok(file) => {
                    e.status(Status::Ok);
                    e.add_length(file.size());
                    if !settings.overrides_content_type {
                        let mime = file.get_inner().get_mime();
                        match (&mime.0, &settings.text_charset) {
                            (&TopLevel::Text, &Some(ref enc)) => {
                                e.format_header("Content-Type", format_args!(
                                    "{}/{}; charset={}", mime.0, mime.1, enc));
                            }
                            _ => {
                                e.format_header("Content-Type", mime);
                            }
                        }
                    }
                    e.add_extra_headers(&settings.extra_headers);
                    if e.done_headers() {
                        Box::new(e.raw_body()
                            .and_then(|raw_body| file.write_into(raw_body))
                            .map(|raw_body| raw_body.done())
                            .map_err(FileError::Sendfile)
                            .map_err(Error::custom))
                        as Reply<_>
                    } else {
                        Box::new(ok(e.done()))
                    }
                }
                Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
                    Box::new(error_page(Status::NotFound, e))
                }
                // One of the known `Other` issues is when path refers to
                // a directory rather than regular file
                Err(ref err) if err.kind() == io::ErrorKind::Other => {
                    Box::new(error_page(Status::Forbidden, e))
                }
                // TODO(tailhook) find out if we want to expose other
                // errors, for example "Permission denied" and "is a directory"
                Err(_) => {
                    Box::new(error_page(Status::InternalServerError, e))
                }
            }))
    })
}

fn from_hex(b: u8) -> Result<u8, ()> {
    match b {
        b'0'...b'9' => Ok(b & 0x0f),
        b'a'...b'f' | b'A'...b'F' => Ok((b & 0x0f) + 9),
        _ => Err(())
    }
}

fn decode_component(buf: &mut Vec<u8>, component: &str) -> Result<(), ()>
{
    let mut chariter = component.as_bytes().iter();
    while let Some(c) = chariter.next() {
        match *c {
            b'%' => {
                let h = from_hex(*chariter.next().ok_or(())?)?;
                let l = from_hex(*chariter.next().ok_or(())?)?;
                let b = (h << 4) | l;
                if b == 0 || b == b'/' {
                    return Err(());
                }
                buf.push(b);
            }
            0 => return Err(()),
            c => buf.push(c),
        }
    }
    Ok(())
}

fn path(settings: &Static, inp: &Input) -> Result<PathBuf, ()> {
    let path = match settings.mode {
        Mode::relative_to_domain_root | Mode::with_hostname => {
            inp.headers.path().unwrap_or("/")
        }
        Mode::relative_to_route => inp.suffix,
    };
    let path = match path.find(|c| c == '?' || c == '#') {
        Some(idx) => &path[..idx],
        None => path
    };
    let mut buf = Vec::with_capacity(path.len());
    if settings.mode == Mode::with_hostname {
        match inp.headers.host()  {
            Some(host) => {
                if host.find("/").is_some() {
                    // no slashes allowed
                    return Err(());
                }
                let name: &str = if let Some(colon) = host.find(":") {
                    &host[..colon]
                } else {
                    &host[..]
                };
                let name = if let Some(ref suf) = settings.strip_host_suffix {
                    if suf.len() >= name.len() {
                        // empty prefix is not allowed yet
                        return Err(());
                    }
                    if !name.ends_with(suf) {
                        // only this suffix should work
                        return Err(());
                    }
                    let final_dot = name.len() - suf.len() - 1;
                    if !name[final_dot..].starts_with('.') {
                        return Err(())
                    }
                    &name[..final_dot]
                } else {
                    name
                };
                buf.extend(name.as_bytes());
            }
            None => return Err(()),
        }
    }
    for cmp in path.split("/") {
        match cmp {
            "" | "." | "%2e" | "%2E" => {},
            ".." | "%2e." | "%2E." | ".%2e" | ".%2E"
            | "%2e%2e" | "%2E%2e" | "%2e%2E" | "%2E%2E" => return Err(()),
            _ => {
                if buf.len() > 0 {
                    buf.push(b'/');
                }
                decode_component(&mut buf, cmp)?;
            }
        }
    }

    // assert that we're not serving from root, this is a security check
    assert!(buf.len() == 0 || buf[0] != b'/');

    // only valid utf-8 supported so far
    let utf8 = from_utf8(&buf).map_err(|_| ())?;
    Ok(settings.path.join(utf8))
}

pub fn serve_file<S: Transport>(settings: &Arc<SingleFile>, mut inp: Input)
    -> Request<S>
{
    if !inp.headers.path().is_some() {
        // Star or authority
        return serve_error_page(Status::Forbidden, inp);
    };
    inp.debug.set_fs_path(&settings.path);
    let pool = get_pool(&inp.runtime, &settings.pool);
    let settings = settings.clone();
    reply(inp, move |mut e| {
        Box::new(pool.open(settings.path.clone())
            .then(move |res| match res {
                Ok(file) => {
                    e.status(Status::Ok);
                    e.add_length(file.size());
                    e.add_header("Content-Type", &settings.content_type);
                    e.add_extra_headers(&settings.extra_headers);
                    if e.done_headers() {
                        Box::new(e.raw_body()
                            .and_then(|raw_body| file.write_into(raw_body))
                            .map(|raw_body| raw_body.done())
                            .map_err(FileError::Sendfile)
                            .map_err(Error::custom))
                        as Reply<_>
                    } else {
                        Box::new(ok(e.done()))
                    }
                }
                Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
                    Box::new(error_page(Status::NotFound, e))
                }
                // TODO(tailhook) find out if we want to expose other
                // errors, for example "Permission denied" and "is a directory"
                Err(_) => {
                    // TODO: log error.
                    Box::new(error_page(Status::InternalServerError, e))
                }
            }))
    })
}

fn new_pool(name: &DiskPoolName, cfg: &config::Disk, meter: &Meter)
    -> DiskPool
{
    let m1 = meter.clone();
    let m2 = meter.clone();
    DiskPool::new(futures_cpupool::Builder::new()
        .pool_size(cfg.num_threads)
        .name_prefix(format!("disk-{}-", name))
        .after_start(move || m1.track_current_thread_by_name())
        .before_stop(move || m2.untrack_current_thread())
        .create())
}

fn get_pool(runtime: &Runtime, name: &DiskPoolName) -> DiskPool {
    let pools = runtime.disk_pools.0.read().expect("readlock for pools");
    match pools.pools.get(name) {
        Some(&(_, ref x)) => x.clone(),
        None => {
            warn!("Unknown disk pool {}, using default", name);
            pools.default.clone()
        }
    }
}

impl DiskPools {
    pub fn new(meter: &Meter) -> DiskPools {
        let mut pools = HashMap::new();
        let cfg = config::Disk {
            num_threads: 40,
        };
        let mut hasher = DefaultHasher::new();
        cfg.hash(&mut hasher);
        let hash = hasher.finish();
        let dname = DiskPoolName::from("default");
        let default = new_pool(&dname, &cfg, meter);
        pools.insert(dname, (hash, default.clone()));

        DiskPools(Arc::new(RwLock::new(PoolsInternal {
            pools: pools,
            default: default,
            meter: meter.clone(),
        })))
    }
    pub fn update(&self, config: &HashMap<DiskPoolName, config::Disk>) {
        let mut pools = &mut *self.0.write().expect("writelock for pools");
        for (name, props) in config {
            let mut hasher = DefaultHasher::new();
            props.hash(&mut hasher);
            let new_hash = hasher.finish();
            match pools.pools.entry(name.clone()) {
                Occupied(mut o) => {
                    let (ref mut old_hash, ref mut old_pool) = *o.get_mut();
                    debug!("Upgrading disk pool {} to {:?}", name, props);
                    if *old_hash != new_hash {
                        *old_pool = new_pool(name, props, &pools.meter);
                        *old_hash = new_hash;
                    }
                }
                Vacant(v) => {
                    v.insert((new_hash, new_pool(name, props, &pools.meter)));
                }
            }
        }
        pools.default = pools.pools[&DiskPoolName::from("default")].1.clone();
    }
}

impl PathOpen {
    fn new(path: PathBuf, settings: &Arc<Static>) -> PathOpen {
        PathOpen {
            path: path,
            settings: settings.clone(),
            file: None,
        }
    }
    fn get_mime(&self) -> &Mime {
        self.file.as_ref()
            .map(|&(_, _, ref m)| m)
            .unwrap()
    }
}

impl IntoFileOpener for PathOpen {
    type Opener = PathOpen;
    fn into_file_opener(self) -> Self::Opener {
        self
    }
}

fn find_index(path: &Path, settings: &Arc<Static>)
    -> Result<(File, u64, Mime), io::Error>
{
    for file_name in &settings.index_files {
        let file = match File::open(path.join(file_name)) {
            Ok(x) => x,
            Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
                continue;
            }
            Err(e) => return Err(e),
        };
        let meta = file.metadata()?;
        if meta.is_file() {
            let mime = guess_mime_type(&file_name);
            return Ok((file, meta.len(), mime));
        }
    }
    return Err(io::ErrorKind::Other.into());
}

impl FileOpener for PathOpen {
    fn open(&mut self) -> Result<(&FileReader, u64), io::Error> {
        if self.file.is_none() {
            let file = File::open(&self.path)?;
            let meta = file.metadata()?;
            if meta.is_dir() {
                if self.settings.index_files.len() > 0 &&
                    metadata(&self.path)?.is_dir()
                {
                    self.file = Some(find_index(&self.path, &self.settings)?);
                } else {
                    return Err(io::ErrorKind::Other.into());
                }
            } else {
                let mime = guess_mime_type(&self.path);
                self.file = Some((file, meta.len(), mime));
            }
        }
        Ok(self.file.as_ref()
            .map(|&(ref f, s, _)| (f as &FileReader, s)).unwrap())
    }
}
<commit_msg>Trying to fix windows<commit_after>use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::fs::{File, metadata};
use std::ffi::OsStr;
use std::hash::{Hash, Hasher};
use std::io;
use std::path::{Path, PathBuf, Component};
use std::sync::{Arc, Mutex, RwLock};
use std::str::from_utf8;

use futures_cpupool;
use futures::{Future};
use futures::future::{ok};
use mime_guess::guess_mime_type;
use mime::{TopLevel, Mime};
use tk_http::server::Error;
use tk_http::Status;
use tk_sendfile::{DiskPool, FileOpener, IntoFileOpener, FileReader};
use self_meter_http::Meter;

use config;
use config::static_files::{Static, Mode, SingleFile};
use default_error_page::{serve_error_page, error_page};
use incoming::{Input, Request, Reply, Transport};
use incoming::reply;
use intern::{DiskPoolName};
use runtime::Runtime;

quick_error! {
    #[derive(Debug)]
    enum FileError {
        Sendfile(err: io::Error) {
            description("sendfile error")
            cause(err)
        }
    }
}

#[cfg(unix)]
struct PathOpen {
    path: PathBuf,
    settings: Arc<Static>,
    file: Option<(File, u64, Mime)>,
}

#[cfg(windows)]
struct PathOpen {
    path: PathBuf,
    settings: Arc<Static>,
    file: Option<(Mutex<File>, u64, Mime)>,
}

#[derive(Clone)]
pub struct DiskPools(Arc<RwLock<PoolsInternal>>);

struct PoolsInternal {
    pools: HashMap<DiskPoolName, (u64, DiskPool)>,
    default: DiskPool,
    meter: Meter,
}

pub fn serve_dir<S: Transport>(settings: &Arc<Static>, mut inp: Input)
    -> Request<S>
{
    // TODO(tailhook) check for symlink attacks
    let path = match path(settings, &inp) {
        Ok(p) => p,
        Err(()) => {
            return serve_error_page(Status::Forbidden, inp);
        }
    };
    inp.debug.set_fs_path(&path);
    let pool = get_pool(&inp.runtime, &settings.pool);
    let settings = settings.clone();
    reply(inp, move |mut e| {
        Box::new(pool.open(PathOpen::new(path, &settings))
            .then(move |res| match res {
                Ok(file) => {
                    e.status(Status::Ok);
                    e.add_length(file.size());
                    if !settings.overrides_content_type {
                        let mime = file.get_inner().get_mime();
                        match (&mime.0, &settings.text_charset) {
                            (&TopLevel::Text, &Some(ref enc)) => {
                                e.format_header("Content-Type", format_args!(
                                    "{}/{}; charset={}", mime.0, mime.1, enc));
                            }
                            _ => {
                                e.format_header("Content-Type", mime);
                            }
                        }
                    }
                    e.add_extra_headers(&settings.extra_headers);
                    if e.done_headers() {
                        Box::new(e.raw_body()
                            .and_then(|raw_body| file.write_into(raw_body))
                            .map(|raw_body| raw_body.done())
                            .map_err(FileError::Sendfile)
                            .map_err(Error::custom))
                        as Reply<_>
                    } else {
                        Box::new(ok(e.done()))
                    }
                }
                Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
                    Box::new(error_page(Status::NotFound, e))
                }
                // One of the known `Other` issues is when path refers to
                // a directory rather than regular file
                Err(ref err) if err.kind() == io::ErrorKind::Other => {
                    Box::new(error_page(Status::Forbidden, e))
                }
                // TODO(tailhook) find out if we want to expose other
                // errors, for example "Permission denied" and "is a directory"
                Err(_) => {
                    Box::new(error_page(Status::InternalServerError, e))
                }
            }))
    })
}

fn from_hex(b: u8) -> Result<u8, ()> {
    match b {
        b'0'...b'9' => Ok(b & 0x0f),
        b'a'...b'f' | b'A'...b'F' => Ok((b & 0x0f) + 9),
        _ => Err(())
    }
}

fn decode_component(buf: &mut Vec<u8>, component: &str) -> Result<(), ()>
{
    let mut chariter = component.as_bytes().iter();
    while let Some(c) = chariter.next() {
        match *c {
            b'%' => {
                let h = from_hex(*chariter.next().ok_or(())?)?;
                let l = from_hex(*chariter.next().ok_or(())?)?;
                let b = (h << 4) | l;
                if b == 0 || b == b'/' {
                    return Err(());
                }
                buf.push(b);
            }
            0 => return Err(()),
            c => buf.push(c),
        }
    }
    Ok(())
}

fn path(settings: &Static, inp: &Input) -> Result<PathBuf, ()> {
    let path = match settings.mode {
        Mode::relative_to_domain_root | Mode::with_hostname => {
            inp.headers.path().unwrap_or("/")
        }
        Mode::relative_to_route => inp.suffix,
    };
    let path = match path.find(|c| c == '?' || c == '#') {
        Some(idx) => &path[..idx],
        None => path
    };
    let mut buf = Vec::with_capacity(path.len());
    if settings.mode == Mode::with_hostname {
        match inp.headers.host()  {
            Some(host) => {
                if host.find("/").is_some() {
                    // no slashes allowed
                    return Err(());
                }
                let name: &str = if let Some(colon) = host.find(":") {
                    &host[..colon]
                } else {
                    &host[..]
                };
                let name = if let Some(ref suf) = settings.strip_host_suffix {
                    if suf.len() >= name.len() {
                        // empty prefix is not allowed yet
                        return Err(());
                    }
                    if !name.ends_with(suf) {
                        // only this suffix should work
                        return Err(());
                    }
                    let final_dot = name.len() - suf.len() - 1;
                    if !name[final_dot..].starts_with('.') {
                        return Err(())
                    }
                    &name[..final_dot]
                } else {
                    name
                };
                buf.extend(name.as_bytes());
            }
            None => return Err(()),
        }
    }
    for cmp in path.split("/") {
        match cmp {
            "" | "." | "%2e" | "%2E" => {},
            ".." | "%2e." | "%2E." | ".%2e" | ".%2E"
            | "%2e%2e" | "%2E%2e" | "%2e%2E" | "%2E%2E" => return Err(()),
            _ => {
                if buf.len() > 0 {
                    buf.push(b'/');
                }
                decode_component(&mut buf, cmp)?;
            }
        }
    }

    // assert that we're not serving from root, this is a security check
    assert!(buf.len() == 0 || buf[0] != b'/');

    // only valid utf-8 supported so far
    let utf8 = from_utf8(&buf).map_err(|_| ())?;
    Ok(settings.path.join(utf8))
}

pub fn serve_file<S: Transport>(settings: &Arc<SingleFile>, mut inp: Input)
    -> Request<S>
{
    if !inp.headers.path().is_some() {
        // Star or authority
        return serve_error_page(Status::Forbidden, inp);
    };
    inp.debug.set_fs_path(&settings.path);
    let pool = get_pool(&inp.runtime, &settings.pool);
    let settings = settings.clone();
    reply(inp, move |mut e| {
        Box::new(pool.open(settings.path.clone())
            .then(move |res| match res {
                Ok(file) => {
                    e.status(Status::Ok);
                    e.add_length(file.size());
                    e.add_header("Content-Type", &settings.content_type);
                    e.add_extra_headers(&settings.extra_headers);
                    if e.done_headers() {
                        Box::new(e.raw_body()
                            .and_then(|raw_body| file.write_into(raw_body))
                            .map(|raw_body| raw_body.done())
                            .map_err(FileError::Sendfile)
                            .map_err(Error::custom))
                        as Reply<_>
                    } else {
                        Box::new(ok(e.done()))
                    }
                }
                Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
                    Box::new(error_page(Status::NotFound, e))
                }
                // TODO(tailhook) find out if we want to expose other
                // errors, for example "Permission denied" and "is a directory"
                Err(_) => {
                    // TODO: log error.
                    Box::new(error_page(Status::InternalServerError, e))
                }
            }))
    })
}

fn new_pool(name: &DiskPoolName, cfg: &config::Disk, meter: &Meter)
    -> DiskPool
{
    let m1 = meter.clone();
    let m2 = meter.clone();
    DiskPool::new(futures_cpupool::Builder::new()
        .pool_size(cfg.num_threads)
        .name_prefix(format!("disk-{}-", name))
        .after_start(move || m1.track_current_thread_by_name())
        .before_stop(move || m2.untrack_current_thread())
        .create())
}

fn get_pool(runtime: &Runtime, name: &DiskPoolName) -> DiskPool {
    let pools = runtime.disk_pools.0.read().expect("readlock for pools");
    match pools.pools.get(name) {
        Some(&(_, ref x)) => x.clone(),
        None => {
            warn!("Unknown disk pool {}, using default", name);
            pools.default.clone()
        }
    }
}

impl DiskPools {
    pub fn new(meter: &Meter) -> DiskPools {
        let mut pools = HashMap::new();
        let cfg = config::Disk {
            num_threads: 40,
        };
        let mut hasher = DefaultHasher::new();
        cfg.hash(&mut hasher);
        let hash = hasher.finish();
        let dname = DiskPoolName::from("default");
        let default = new_pool(&dname, &cfg, meter);
        pools.insert(dname, (hash, default.clone()));

        DiskPools(Arc::new(RwLock::new(PoolsInternal {
            pools: pools,
            default: default,
            meter: meter.clone(),
        })))
    }
    pub fn update(&self, config: &HashMap<DiskPoolName, config::Disk>) {
        let mut pools = &mut *self.0.write().expect("writelock for pools");
        for (name, props) in config {
            let mut hasher = DefaultHasher::new();
            props.hash(&mut hasher);
            let new_hash = hasher.finish();
            match pools.pools.entry(name.clone()) {
                Occupied(mut o) => {
                    let (ref mut old_hash, ref mut old_pool) = *o.get_mut();
                    debug!("Upgrading disk pool {} to {:?}", name, props);
                    if *old_hash != new_hash {
                        *old_pool = new_pool(name, props, &pools.meter);
                        *old_hash = new_hash;
                    }
                }
                Vacant(v) => {
                    v.insert((new_hash, new_pool(name, props, &pools.meter)));
                }
            }
        }
        pools.default = pools.pools[&DiskPoolName::from("default")].1.clone();
    }
}

impl PathOpen {
    fn new(path: PathBuf, settings: &Arc<Static>) -> PathOpen {
        PathOpen {
            path: path,
            settings: settings.clone(),
            file: None,
        }
    }
    fn get_mime(&self) -> &Mime {
        self.file.as_ref()
            .map(|&(_, _, ref m)| m)
            .unwrap()
    }
}

impl IntoFileOpener for PathOpen {
    type Opener = PathOpen;
    fn into_file_opener(self) -> Self::Opener {
        self
    }
}

fn find_index(path: &Path, settings: &Arc<Static>)
    -> Result<(File, u64, Mime), io::Error>
{
    for file_name in &settings.index_files {
        let file = match File::open(path.join(file_name)) {
            Ok(x) => x,
            Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
                continue;
            }
            Err(e) => return Err(e),
        };
        let meta = file.metadata()?;
        if meta.is_file() {
            let mime = guess_mime_type(&file_name);
            return Ok((file, meta.len(), mime));
        }
    }
    return Err(io::ErrorKind::Other.into());
}

#[cfg(unix)]
fn wrap_file(file: File) -> File {
    file
}

#[cfg(windows)]
fn wrap_file(file: File) -> Mutex<File> {
    Mutex::new(file)
}

impl FileOpener for PathOpen {
    fn open(&mut self) -> Result<(&FileReader, u64), io::Error> {
        if self.file.is_none() {
            let file = File::open(&self.path)?;
            let meta = file.metadata()?;
            if meta.is_dir() {
                if self.settings.index_files.len() > 0 &&
                    metadata(&self.path)?.is_dir()
                {
                    let (f, mt, mm) = find_index(&self.path, &self.settings)?;
                    self.file = Some((wrap_file(f), mt, mm));
                } else {
                    return Err(io::ErrorKind::Other.into());
                }
            } else {
                let mime = guess_mime_type(&self.path);
                self.file = Some((wrap_file(file), meta.len(), mime));
            }
        }
        Ok(self.file.as_ref()
            .map(|&(ref f, s, _)| (f as &FileReader, s)).unwrap())
    }
}
<|endoftext|>


<commit_before>
//! Helper functions for hue and saturation selection

use image::{
    ImageBuf,
    MutableRefImage,
    Rgba,
    SubImage,
};

/// Selects color at coordinate in a rectangle show hue and saturation
pub fn select(rect: [u32, ..4], x: u32, y: u32) -> Rgba<u8> {
    let inv_rect_width = 1.0_f64 / rect[2] as f64;
    let half_inv_rect_height = 0.5_f64 / rect[3] as f64;
    let inv60 = 1.0_f64 / 60.0_f64;

    let fx = (x - rect[0]) as f64 * inv_rect_width;
    let hue = fx * 360.0_f64;
    // Saturation is 0.5 * fy, so by halving the height we save operations.
    let saturation = (y - rect[1]) as f64 * half_inv_rect_height;
    let hi = (hue * inv60) as int;
    let f = hue * inv60 - hi as f64;
    let hi = hi % 6;
    let v = 255; // use white as color without saturation.
    let p = (255.0 * (1.0 - saturation)) as u8;
    let q = (255.0 * (1.0 - f * saturation)) as u8;
    let t = (255.0 * (1.0 - (1.0 - f) * saturation)) as u8;
    Rgba(
        match hi {
            0 | 1 => p,
            2 => t,
            3 | 4 => v,
            _ => q
        },
        match hi {
            0 => t,
            1 | 2 => v,
            3 => q,
            _ => p
        },
        match hi {
            1 => q,
            2 | 3 => p,
            4 => t,
            _ => v
        },
        255
    )
}

/// Sets colors to hue/saturation image.
/// White is used as color without saturation.
/// This is at the top of the image,
/// while saturated colors are at the bottom.
pub fn fill_image(
    image: &mut ImageBuf<Rgba<u8>>,
    rect: [u32, ..4]
) {
    let mut sub_image = SubImage::new(image,
        rect[0], rect[1], rect[2], rect[3]);
    let inv_rect_width = 1.0_f64 / rect[2] as f64;
    let half_inv_rect_height = 0.5_f64 / rect[3] as f64;
    let inv60 = 1.0_f64 / 60.0_f64;
    for (x, y, pxl) in sub_image.pixels_mut() {
        let fx = (x - rect[0]) as f64 * inv_rect_width;
        let hue = fx * 360.0_f64;
        // Saturation is 0.5 * fy, so by halving the height we save operations.
        let saturation = (y - rect[1]) as f64 * half_inv_rect_height;
        let hi = (hue * inv60) as int;
        let f = hue * inv60 - hi as f64;
        let hi = hi % 6;
        let v = 255; // use white as color without saturation.
        let p = (255.0 * (1.0 - saturation)) as u8;
        let q = (255.0 * (1.0 - f * saturation)) as u8;
        let t = (255.0 * (1.0 - (1.0 - f) * saturation)) as u8;
        *pxl = Rgba(
            match hi {
                0 | 1 => p,
                2 => t,
                3 | 4 => v,
                _ => q
            },
            match hi {
                0 => t,
                1 | 2 => v,
                3 => q,
                _ => p
            },
            match hi {
                1 => q,
                2 | 3 => p,
                4 => t,
                _ => v
            },
            255
        );
    }
}
<commit_msg>Update due to PistonDevelopers/image changes<commit_after>
//! Helper functions for hue and saturation selection

use image::{
    ImageBuf,
    GenericImage,
    Rgba,
    SubImage,
};

/// Selects color at coordinate in a rectangle show hue and saturation
pub fn select(rect: [u32, ..4], x: u32, y: u32) -> Rgba<u8> {
    let inv_rect_width = 1.0_f64 / rect[2] as f64;
    let half_inv_rect_height = 0.5_f64 / rect[3] as f64;
    let inv60 = 1.0_f64 / 60.0_f64;

    let fx = (x - rect[0]) as f64 * inv_rect_width;
    let hue = fx * 360.0_f64;
    // Saturation is 0.5 * fy, so by halving the height we save operations.
    let saturation = (y - rect[1]) as f64 * half_inv_rect_height;
    let hi = (hue * inv60) as int;
    let f = hue * inv60 - hi as f64;
    let hi = hi % 6;
    let v = 255; // use white as color without saturation.
    let p = (255.0 * (1.0 - saturation)) as u8;
    let q = (255.0 * (1.0 - f * saturation)) as u8;
    let t = (255.0 * (1.0 - (1.0 - f) * saturation)) as u8;
    Rgba(
        match hi {
            0 | 1 => p,
            2 => t,
            3 | 4 => v,
            _ => q
        },
        match hi {
            0 => t,
            1 | 2 => v,
            3 => q,
            _ => p
        },
        match hi {
            1 => q,
            2 | 3 => p,
            4 => t,
            _ => v
        },
        255
    )
}

/// Sets colors to hue/saturation image.
/// White is used as color without saturation.
/// This is at the top of the image,
/// while saturated colors are at the bottom.
pub fn fill_image(
    image: &mut ImageBuf<Rgba<u8>>,
    rect: [u32, ..4]
) {
    let mut sub_image = SubImage::new(image,
        rect[0], rect[1], rect[2], rect[3]);
    let inv_rect_width = 1.0_f64 / rect[2] as f64;
    let half_inv_rect_height = 0.5_f64 / rect[3] as f64;
    let inv60 = 1.0_f64 / 60.0_f64;
    for (x, y, pxl) in sub_image.pixels_mut() {
        let fx = (x - rect[0]) as f64 * inv_rect_width;
        let hue = fx * 360.0_f64;
        // Saturation is 0.5 * fy, so by halving the height we save operations.
        let saturation = (y - rect[1]) as f64 * half_inv_rect_height;
        let hi = (hue * inv60) as int;
        let f = hue * inv60 - hi as f64;
        let hi = hi % 6;
        let v = 255; // use white as color without saturation.
        let p = (255.0 * (1.0 - saturation)) as u8;
        let q = (255.0 * (1.0 - f * saturation)) as u8;
        let t = (255.0 * (1.0 - (1.0 - f) * saturation)) as u8;
        *pxl = Rgba(
            match hi {
                0 | 1 => p,
                2 => t,
                3 | 4 => v,
                _ => q
            },
            match hi {
                0 => t,
                1 | 2 => v,
                3 => q,
                _ => p
            },
            match hi {
                1 => q,
                2 | 3 => p,
                4 => t,
                _ => v
            },
            255
        );
    }
}
<|endoftext|>