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

use ShareGlContext = sharegl::platform::Context;
use dom::event::Event;
use platform::resize_rate_limiter::ResizeRateLimiter;

use azure::azure_hl::{BackendType, B8G8R8A8, DataSourceSurface, DrawTarget, SourceSurfaceMethods};
use core::comm::{Chan, SharedChan, Port};
use core::util;
use geom::matrix::identity;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::compositor::{Compositor, LayerBuffer, LayerBufferSet};
use gfx::opts::Opts;
use gfx::util::time;
use core::cell::Cell;
use glut::glut;
use layers;
use sharegl;
use sharegl::ShareGlContext;
use sharegl::base::ShareContext;

pub struct OSMain {
    chan: SharedChan<Msg>
}

impl Clone for OSMain {
    fn clone(&self) -> OSMain {
        OSMain {
            chan: self.chan.clone()
        }
    }
}

// FIXME: Move me over to opts.rs.
enum Mode {
	GlutMode,
	ShareMode
}

enum Window {
	GlutWindow(glut::Window),
	ShareWindow(ShareGlContext)
}

pub enum Msg {
    BeginDrawing(comm::Chan<LayerBufferSet>),
    Draw(comm::Chan<LayerBufferSet>, LayerBufferSet),
    AddKeyHandler(comm::Chan<()>),
    Exit
}

pub fn OSMain(dom_event_chan: comm::SharedChan<Event>, opts: Opts) -> OSMain {
    let dom_event_chan = Cell(dom_event_chan);
    OSMain {
        chan: SharedChan(on_osmain::<Msg>(|po| {
            let po = Cell(po);
            do platform::runmain {
                debug!("preparing to enter main loop");

                // FIXME: Use the servo options.
                let mode;
                match os::getenv("SERVO_SHARE") {
                    Some(_) => mode = ShareMode,
                    None => mode = GlutMode
                }

                mainloop(mode, po.take(), dom_event_chan.take(), &opts);
            }
        }))
    }
}

/// Azure surface wrapping to work with the layers infrastructure.
struct AzureDrawTargetImageData {
    draw_target: DrawTarget,
    data_source_surface: DataSourceSurface,
    size: Size2D<uint>
}

impl layers::layers::ImageData for AzureDrawTargetImageData {
    fn size(&self) -> Size2D<uint> { self.size }
    fn stride(&self) -> uint { self.data_source_surface.get_size().width as uint }
    fn format(&self) -> layers::layers::Format {
        // FIXME: This is not always correct. We should query the Azure draw target for the format.
        layers::layers::ARGB32Format
    }
    fn with_data(&self, f: layers::layers::WithDataFn) {
        do self.data_source_surface.with_data |data| {
            f(data);
        }
    }
}

fn mainloop(mode: Mode,
            po: Port<Msg>,
            dom_event_chan: SharedChan<Event>,
            opts: &Opts) {
    let key_handlers: @mut ~[Chan<()>] = @mut ~[];

	let window;
	match mode {
		GlutMode => {
			glut::init();
			glut::init_display_mode(glut::DOUBLE);
			let glut_window = glut::create_window(~"Servo");
			glut::reshape_window(glut_window, 800, 600);
			window = GlutWindow(glut_window);
		}
		ShareMode => {
            let size = Size2D(800, 600);
			let share_context: ShareGlContext = sharegl::base::ShareContext::new(size);
			io::println(fmt!("Sharing ID is %d", share_context.id()));
			window = ShareWindow(share_context);
		}
	}

    let surfaces = @mut SurfaceSet(opts.render_backend);

    let context = layers::rendergl::init_render_context();

    let root_layer = @mut layers::layers::ContainerLayer();
    let original_layer_transform;
    {
        let image_data = @layers::layers::BasicImageData::new(Size2D(0u, 0u),
                                                              0,
                                                              layers::layers::RGB24Format,
                                                              ~[]);
        let image = @mut layers::layers::Image::new(image_data as @layers::layers::ImageData);
        let image_layer = @mut layers::layers::ImageLayer(image);
        original_layer_transform = image_layer.common.transform;
        image_layer.common.set_transform(original_layer_transform.scale(800.0, 600.0, 1.0));
        root_layer.add_child(layers::layers::ImageLayerKind(image_layer));
    }

    let scene = @layers::scene::Scene(layers::layers::ContainerLayerKind(root_layer),
                                      Size2D(800.0, 600.0),
                                      identity());

    let done = @mut false;
    let resize_rate_limiter = @mut ResizeRateLimiter(dom_event_chan);
    let check_for_messages: @fn() = || {

        // Periodically check if content responded to our last resize event
        resize_rate_limiter.check_resize_response();

        // Handle messages
        //#debug("osmain: peeking");
        while po.peek() {
            match po.recv() {
                AddKeyHandler(key_ch) => key_handlers.push(key_ch),
                BeginDrawing(sender) => lend_surface(surfaces, sender),
                Draw(sender, draw_target) => {
                    debug!("osmain: received new frame");
                    return_surface(surfaces, draw_target);
                    lend_surface(surfaces, sender);

                    // Iterate over the children of the container layer.
                    let mut current_layer_child = root_layer.first_child;

                    // Replace the image layer data with the buffer data.
                    let buffers = util::replace(&mut surfaces.front.layer_buffer_set.buffers, ~[]);
                    for buffers.each |buffer| {
                        let width = buffer.rect.size.width as uint;
                        let height = buffer.rect.size.height as uint;

                        debug!("osmain: compositing buffer rect %?", &buffer.rect);

                        let image_data = @AzureDrawTargetImageData {
                            draw_target: buffer.draw_target.clone(),
                            data_source_surface: buffer.draw_target.snapshot().get_data_surface(),
                            size: Size2D(width, height)
                        };
                        let image = @mut layers::layers::Image::new(image_data as @layers::layers::ImageData);

                        // Find or create an image layer.
                        let image_layer;
                        current_layer_child = match current_layer_child {
                            None => {
                                debug!("osmain: adding new image layer");
                                image_layer = @mut layers::layers::ImageLayer(image);
                                root_layer.add_child(layers::layers::ImageLayerKind(image_layer));
                                None
                            }
                            Some(layers::layers::ImageLayerKind(existing_image_layer)) => {
                                image_layer = existing_image_layer;
                                image_layer.set_image(image);

                                // Move on to the next sibling.
                                do current_layer_child.get().with_common |common| {
                                    common.next_sibling
                                }
                            }
                            Some(_) => {
                                fail!(~"found unexpected layer kind")
                            }
                        };

                        // Set the layer's transform.
                        let x = buffer.rect.origin.x as f32;
                        let y = buffer.rect.origin.y as f32;
                        image_layer.common.set_transform(
                            original_layer_transform.translate(x, y, 0.0)
                                .scale(width as f32, height as f32, 1.0));
                    }
                    surfaces.front.layer_buffer_set.buffers = buffers;
                }
                Exit => {
                    *done = true;
                }
            }
        }
    };

    let adjust_for_window_resizing: @fn() = || {
        let window_width = glut::get(glut::WindowWidth) as uint;
        let window_height = glut::get(glut::WindowHeight) as uint;

        // FIXME: Cross-crate struct mutability is broken.
        let size: &mut Size2D<f32>;
        unsafe { size = cast::transmute(&scene.size); }
        *size = Size2D(window_width as f32, window_height as f32);
    };

    let composite: @fn() = || {
        //#debug("osmain: drawing to screen");

        do time::time(~"compositing") {
            adjust_for_window_resizing();
            layers::rendergl::render_scene(context, scene);
        }

        glut::swap_buffers();
        glut::post_redisplay();
    };

    match window {
        GlutWindow(window) => {
            do glut::reshape_func(window) |width, height| {
                debug!("osmain: window resized to %d,%d", width as int, height as int);
                check_for_messages();
                resize_rate_limiter.window_resized(width as uint, height as uint);
                //composite();
            }

            do glut::display_func() {
                //debug!("osmain: display func");
                check_for_messages();
                composite();
            }

            while !*done {
                //#debug("osmain: running GLUT check loop");
                glut::check_loop();
            }
        }
        ShareWindow(share_context) => {
            loop {
                check_for_messages();
                do time::time(~"compositing") {
                    layers::rendergl::render_scene(context, scene);
                }

                share_context.flush();
            }
        }
    }
}

/**
Implementation to allow the osmain channel to be used as a graphics
compositor for the renderer
*/
impl Compositor for OSMain {
    fn begin_drawing(&self, next_dt: comm::Chan<LayerBufferSet>) {
        self.chan.send(BeginDrawing(next_dt))
    }
    fn draw(&self, next_dt: comm::Chan<LayerBufferSet>, draw_me: LayerBufferSet) {
        self.chan.send(Draw(next_dt, draw_me))
    }
}

struct SurfaceSet {
    front: Surface,
    back: Surface,
}

fn lend_surface(surfaces: &mut SurfaceSet, receiver: comm::Chan<LayerBufferSet>) {
    // We are in a position to lend out the surface?
    assert!(surfaces.front.have);
    // Ok then take it
    let old_layer_buffers = util::replace(&mut surfaces.front.layer_buffer_set.buffers, ~[]);
    let new_layer_buffers = do old_layer_buffers.map |layer_buffer| {
        let draw_target_ref = &layer_buffer.draw_target;
        let layer_buffer = LayerBuffer {
            draw_target: draw_target_ref.clone(),
            rect: copy layer_buffer.rect,
            stride: layer_buffer.stride
        };
        debug!("osmain: lending surface %?", layer_buffer);
        layer_buffer
    };
    surfaces.front.layer_buffer_set.buffers = old_layer_buffers;

    let new_layer_buffer_set = LayerBufferSet { buffers: new_layer_buffers };
    receiver.send(new_layer_buffer_set);
    // Now we don't have it
    surfaces.front.have = false;
    // But we (hopefully) have another!
    surfaces.front <-> surfaces.back;
    // Let's look
    assert!(surfaces.front.have);
}

fn return_surface(surfaces: &mut SurfaceSet, layer_buffer_set: LayerBufferSet) {
    //#debug("osmain: returning surface %?", layer_buffer_set);
    // We have room for a return
    assert!(surfaces.front.have);
    assert!(!surfaces.back.have);

    surfaces.back.layer_buffer_set = layer_buffer_set;

    // Now we have it again
    surfaces.back.have = true;
}

fn SurfaceSet(backend: BackendType) -> SurfaceSet {
    SurfaceSet { front: Surface(backend), back: Surface(backend) }
}

struct Surface {
    layer_buffer_set: LayerBufferSet,
    have: bool,
}

fn Surface(backend: BackendType) -> Surface {
    let layer_buffer = LayerBuffer {
        draw_target: DrawTarget::new(backend, Size2D(800i32, 600i32), B8G8R8A8),
        rect: Rect(Point2D(0u, 0u), Size2D(800u, 600u)),
        stride: 800
    };
    let layer_buffer_set = LayerBufferSet { buffers: ~[ layer_buffer ] };
    Surface { layer_buffer_set: layer_buffer_set, have: true }
}

/// A function for spawning into the platform's main thread
fn on_osmain<T: Owned>(f: ~fn(po: Port<T>)) -> Chan<T> {
    let (setup_po, setup_ch) = comm::stream();
    do task::task().sched_mode(task::PlatformThread).spawn {
        let (po, ch) = comm::stream();
        setup_ch.send(ch);
        f(po);
    }
    setup_po.recv()
}

// #[cfg(target_os = "linux")]
mod platform {
    pub fn runmain(f: &fn()) {
        f()
    }
}

<commit_msg>remove tabs in src/servo/platform/osmain.rs<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/. */

use ShareGlContext = sharegl::platform::Context;
use dom::event::Event;
use platform::resize_rate_limiter::ResizeRateLimiter;

use azure::azure_hl::{BackendType, B8G8R8A8, DataSourceSurface, DrawTarget, SourceSurfaceMethods};
use core::comm::{Chan, SharedChan, Port};
use core::util;
use geom::matrix::identity;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::compositor::{Compositor, LayerBuffer, LayerBufferSet};
use gfx::opts::Opts;
use gfx::util::time;
use core::cell::Cell;
use glut::glut;
use layers;
use sharegl;
use sharegl::ShareGlContext;
use sharegl::base::ShareContext;

pub struct OSMain {
    chan: SharedChan<Msg>
}

impl Clone for OSMain {
    fn clone(&self) -> OSMain {
        OSMain {
            chan: self.chan.clone()
        }
    }
}

// FIXME: Move me over to opts.rs.
enum Mode {
    GlutMode,
    ShareMode
}

enum Window {
    GlutWindow(glut::Window),
    ShareWindow(ShareGlContext)
}

pub enum Msg {
    BeginDrawing(comm::Chan<LayerBufferSet>),
    Draw(comm::Chan<LayerBufferSet>, LayerBufferSet),
    AddKeyHandler(comm::Chan<()>),
    Exit
}

pub fn OSMain(dom_event_chan: comm::SharedChan<Event>, opts: Opts) -> OSMain {
    let dom_event_chan = Cell(dom_event_chan);
    OSMain {
        chan: SharedChan(on_osmain::<Msg>(|po| {
            let po = Cell(po);
            do platform::runmain {
                debug!("preparing to enter main loop");

                // FIXME: Use the servo options.
                let mode;
                match os::getenv("SERVO_SHARE") {
                    Some(_) => mode = ShareMode,
                    None => mode = GlutMode
                }

                mainloop(mode, po.take(), dom_event_chan.take(), &opts);
            }
        }))
    }
}

/// Azure surface wrapping to work with the layers infrastructure.
struct AzureDrawTargetImageData {
    draw_target: DrawTarget,
    data_source_surface: DataSourceSurface,
    size: Size2D<uint>
}

impl layers::layers::ImageData for AzureDrawTargetImageData {
    fn size(&self) -> Size2D<uint> { self.size }
    fn stride(&self) -> uint { self.data_source_surface.get_size().width as uint }
    fn format(&self) -> layers::layers::Format {
        // FIXME: This is not always correct. We should query the Azure draw target for the format.
        layers::layers::ARGB32Format
    }
    fn with_data(&self, f: layers::layers::WithDataFn) {
        do self.data_source_surface.with_data |data| {
            f(data);
        }
    }
}

fn mainloop(mode: Mode,
            po: Port<Msg>,
            dom_event_chan: SharedChan<Event>,
            opts: &Opts) {
    let key_handlers: @mut ~[Chan<()>] = @mut ~[];

    let window;
    match mode {
        GlutMode => {
            glut::init();
            glut::init_display_mode(glut::DOUBLE);
            let glut_window = glut::create_window(~"Servo");
            glut::reshape_window(glut_window, 800, 600);
            window = GlutWindow(glut_window);
        }
        ShareMode => {
            let size = Size2D(800, 600);
            let share_context: ShareGlContext = sharegl::base::ShareContext::new(size);
            io::println(fmt!("Sharing ID is %d", share_context.id()));
            window = ShareWindow(share_context);
        }
    }

    let surfaces = @mut SurfaceSet(opts.render_backend);

    let context = layers::rendergl::init_render_context();

    let root_layer = @mut layers::layers::ContainerLayer();
    let original_layer_transform;
    {
        let image_data = @layers::layers::BasicImageData::new(Size2D(0u, 0u),
                                                              0,
                                                              layers::layers::RGB24Format,
                                                              ~[]);
        let image = @mut layers::layers::Image::new(image_data as @layers::layers::ImageData);
        let image_layer = @mut layers::layers::ImageLayer(image);
        original_layer_transform = image_layer.common.transform;
        image_layer.common.set_transform(original_layer_transform.scale(800.0, 600.0, 1.0));
        root_layer.add_child(layers::layers::ImageLayerKind(image_layer));
    }

    let scene = @layers::scene::Scene(layers::layers::ContainerLayerKind(root_layer),
                                      Size2D(800.0, 600.0),
                                      identity());

    let done = @mut false;
    let resize_rate_limiter = @mut ResizeRateLimiter(dom_event_chan);
    let check_for_messages: @fn() = || {

        // Periodically check if content responded to our last resize event
        resize_rate_limiter.check_resize_response();

        // Handle messages
        //#debug("osmain: peeking");
        while po.peek() {
            match po.recv() {
                AddKeyHandler(key_ch) => key_handlers.push(key_ch),
                BeginDrawing(sender) => lend_surface(surfaces, sender),
                Draw(sender, draw_target) => {
                    debug!("osmain: received new frame");
                    return_surface(surfaces, draw_target);
                    lend_surface(surfaces, sender);

                    // Iterate over the children of the container layer.
                    let mut current_layer_child = root_layer.first_child;

                    // Replace the image layer data with the buffer data.
                    let buffers = util::replace(&mut surfaces.front.layer_buffer_set.buffers, ~[]);
                    for buffers.each |buffer| {
                        let width = buffer.rect.size.width as uint;
                        let height = buffer.rect.size.height as uint;

                        debug!("osmain: compositing buffer rect %?", &buffer.rect);

                        let image_data = @AzureDrawTargetImageData {
                            draw_target: buffer.draw_target.clone(),
                            data_source_surface: buffer.draw_target.snapshot().get_data_surface(),
                            size: Size2D(width, height)
                        };
                        let image = @mut layers::layers::Image::new(image_data as @layers::layers::ImageData);

                        // Find or create an image layer.
                        let image_layer;
                        current_layer_child = match current_layer_child {
                            None => {
                                debug!("osmain: adding new image layer");
                                image_layer = @mut layers::layers::ImageLayer(image);
                                root_layer.add_child(layers::layers::ImageLayerKind(image_layer));
                                None
                            }
                            Some(layers::layers::ImageLayerKind(existing_image_layer)) => {
                                image_layer = existing_image_layer;
                                image_layer.set_image(image);

                                // Move on to the next sibling.
                                do current_layer_child.get().with_common |common| {
                                    common.next_sibling
                                }
                            }
                            Some(_) => {
                                fail!(~"found unexpected layer kind")
                            }
                        };

                        // Set the layer's transform.
                        let x = buffer.rect.origin.x as f32;
                        let y = buffer.rect.origin.y as f32;
                        image_layer.common.set_transform(
                            original_layer_transform.translate(x, y, 0.0)
                                .scale(width as f32, height as f32, 1.0));
                    }
                    surfaces.front.layer_buffer_set.buffers = buffers;
                }
                Exit => {
                    *done = true;
                }
            }
        }
    };

    let adjust_for_window_resizing: @fn() = || {
        let window_width = glut::get(glut::WindowWidth) as uint;
        let window_height = glut::get(glut::WindowHeight) as uint;

        // FIXME: Cross-crate struct mutability is broken.
        let size: &mut Size2D<f32>;
        unsafe { size = cast::transmute(&scene.size); }
        *size = Size2D(window_width as f32, window_height as f32);
    };

    let composite: @fn() = || {
        //#debug("osmain: drawing to screen");

        do time::time(~"compositing") {
            adjust_for_window_resizing();
            layers::rendergl::render_scene(context, scene);
        }

        glut::swap_buffers();
        glut::post_redisplay();
    };

    match window {
        GlutWindow(window) => {
            do glut::reshape_func(window) |width, height| {
                debug!("osmain: window resized to %d,%d", width as int, height as int);
                check_for_messages();
                resize_rate_limiter.window_resized(width as uint, height as uint);
                //composite();
            }

            do glut::display_func() {
                //debug!("osmain: display func");
                check_for_messages();
                composite();
            }

            while !*done {
                //#debug("osmain: running GLUT check loop");
                glut::check_loop();
            }
        }
        ShareWindow(share_context) => {
            loop {
                check_for_messages();
                do time::time(~"compositing") {
                    layers::rendergl::render_scene(context, scene);
                }

                share_context.flush();
            }
        }
    }
}

/**
Implementation to allow the osmain channel to be used as a graphics
compositor for the renderer
*/
impl Compositor for OSMain {
    fn begin_drawing(&self, next_dt: comm::Chan<LayerBufferSet>) {
        self.chan.send(BeginDrawing(next_dt))
    }
    fn draw(&self, next_dt: comm::Chan<LayerBufferSet>, draw_me: LayerBufferSet) {
        self.chan.send(Draw(next_dt, draw_me))
    }
}

struct SurfaceSet {
    front: Surface,
    back: Surface,
}

fn lend_surface(surfaces: &mut SurfaceSet, receiver: comm::Chan<LayerBufferSet>) {
    // We are in a position to lend out the surface?
    assert!(surfaces.front.have);
    // Ok then take it
    let old_layer_buffers = util::replace(&mut surfaces.front.layer_buffer_set.buffers, ~[]);
    let new_layer_buffers = do old_layer_buffers.map |layer_buffer| {
        let draw_target_ref = &layer_buffer.draw_target;
        let layer_buffer = LayerBuffer {
            draw_target: draw_target_ref.clone(),
            rect: copy layer_buffer.rect,
            stride: layer_buffer.stride
        };
        debug!("osmain: lending surface %?", layer_buffer);
        layer_buffer
    };
    surfaces.front.layer_buffer_set.buffers = old_layer_buffers;

    let new_layer_buffer_set = LayerBufferSet { buffers: new_layer_buffers };
    receiver.send(new_layer_buffer_set);
    // Now we don't have it
    surfaces.front.have = false;
    // But we (hopefully) have another!
    surfaces.front <-> surfaces.back;
    // Let's look
    assert!(surfaces.front.have);
}

fn return_surface(surfaces: &mut SurfaceSet, layer_buffer_set: LayerBufferSet) {
    //#debug("osmain: returning surface %?", layer_buffer_set);
    // We have room for a return
    assert!(surfaces.front.have);
    assert!(!surfaces.back.have);

    surfaces.back.layer_buffer_set = layer_buffer_set;

    // Now we have it again
    surfaces.back.have = true;
}

fn SurfaceSet(backend: BackendType) -> SurfaceSet {
    SurfaceSet { front: Surface(backend), back: Surface(backend) }
}

struct Surface {
    layer_buffer_set: LayerBufferSet,
    have: bool,
}

fn Surface(backend: BackendType) -> Surface {
    let layer_buffer = LayerBuffer {
        draw_target: DrawTarget::new(backend, Size2D(800i32, 600i32), B8G8R8A8),
        rect: Rect(Point2D(0u, 0u), Size2D(800u, 600u)),
        stride: 800
    };
    let layer_buffer_set = LayerBufferSet { buffers: ~[ layer_buffer ] };
    Surface { layer_buffer_set: layer_buffer_set, have: true }
}

/// A function for spawning into the platform's main thread
fn on_osmain<T: Owned>(f: ~fn(po: Port<T>)) -> Chan<T> {
    let (setup_po, setup_ch) = comm::stream();
    do task::task().sched_mode(task::PlatformThread).spawn {
        let (po, ch) = comm::stream();
        setup_ch.send(ch);
        f(po);
    }
    setup_po.recv()
}

// #[cfg(target_os = "linux")]
mod platform {
    pub fn runmain(f: &fn()) {
        f()
    }
}

<|endoftext|>


<commit_before>use webcore::value::{Reference, Value};
use webcore::try_from::TryInto;
use webapi::event::{IEvent, Event, ConcreteEvent};

/// The `HashChangeEvent` is fired when the fragment
/// identifier of the URL has changed (the part of the URL
/// that follows the # symbol, including the # symbol).
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/hashchange)
// https://html.spec.whatwg.org/#event-hashchange
// https://html.spec.whatwg.org/#hashchangeevent
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "HashChangeEvent")]
#[reference(subclass_of(Event))]
pub struct HashChangeEvent( Reference );

impl IEvent for HashChangeEvent {}
impl ConcreteEvent for HashChangeEvent {
    const EVENT_TYPE: &'static str = "hashchange";
}

impl HashChangeEvent {
    /// The previous URL from which the window was navigated.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent)
    // https://html.spec.whatwg.org/#the-hashchangeevent-interface:dom-hashchangeevent-oldurl
    #[inline]
    pub fn old_url( &self ) -> String {
        js!(
            return @{self.as_ref()}.oldURL;
        ).try_into().unwrap()
    }

    /// The new URL to which the window was navigated.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent)
    // https://html.spec.whatwg.org/#the-hashchangeevent-interface:dom-hashchangeevent-newurl
    #[inline]
    pub fn new_url( &self ) -> String {
        js!(
            return @{self.as_ref()}.newURL;
        ).try_into().unwrap()
    }
}

/// A popstate event is dispatched to the window every time the active history entry changes
/// between two history entries for the same document. If the history entry being activated was
/// created by a call to history.push_state() or was affected by a call to history.replace_state(),
/// the popstate event's state property contains a copy of the history entry's state object.
///
/// [(Javascript docs)](https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent)
// https://html.spec.whatwg.org/#event-popstate
// https://html.spec.whatwg.org/#popstateevent
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "Event")] // TODO: Better type check.
#[reference(subclass_of(Event))]
pub struct PopStateEvent(Reference);

impl PopStateEvent {
    /// The state object associated to the new history entry, if that entry was created with
    /// push_state or affected by replace_state.
    ///
    /// Example usage:
    ///
    /// ```rust,ignore
    /// let state: Option<MyStruct> = event.state().try_into().ok();
    /// ```
    // https://html.spec.whatwg.org/#dom-popstateevent-state
    #[inline]
    pub fn state(&self) -> Value {
        js!(return @{self}.state;)
    }
}

impl IEvent for PopStateEvent {}

impl ConcreteEvent for PopStateEvent {
    const EVENT_TYPE: &'static str = "popstate";
}

#[cfg(all(test, feature = "web_test"))]
mod tests {
    use super::*;

    #[test]
    fn test_hash_change_event() {
        let event: HashChangeEvent = js!(
            return new HashChangeEvent(
                @{HashChangeEvent::EVENT_TYPE},
                {
                    oldURL: "http://test.com#foo",
                    newURL: "http://test.com#bar"
                }
            );
        ).try_into().unwrap();
        assert_eq!( event.event_type(), HashChangeEvent::EVENT_TYPE );
        assert_eq!( event.old_url(), "http://test.com#foo" );
        assert_eq!( event.new_url(), "http://test.com#bar" );
    }

    #[test]
    fn test_pop_state_event() {
        let event: PopStateEvent = js!(
            return new PopStateEvent(
                @{PopStateEvent::EVENT_TYPE},
                {
                    state: {
                        color: "tomato"
                    }
                }
            );
        ).try_into().unwrap();

        assert_eq!(event.event_type(), PopStateEvent::EVENT_TYPE);

        let state_value: Value = event.state();
        let state: ::std::collections::BTreeMap<String, Value> = state_value
            .as_object()
            .unwrap()
            .into();
        let mut expected = ::std::collections::BTreeMap::new();
        expected.insert("color".to_string(), "tomato".into());

        assert_eq!(state, expected);
    }
}
<commit_msg>Clarify `PopStateEvent` documentation (#195)<commit_after>use webcore::value::{Reference, Value};
use webcore::try_from::TryInto;
use webapi::event::{IEvent, Event, ConcreteEvent};

/// The `HashChangeEvent` is fired when the fragment
/// identifier of the URL has changed (the part of the URL
/// that follows the # symbol, including the # symbol).
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/Events/hashchange)
// https://html.spec.whatwg.org/#event-hashchange
// https://html.spec.whatwg.org/#hashchangeevent
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "HashChangeEvent")]
#[reference(subclass_of(Event))]
pub struct HashChangeEvent( Reference );

impl IEvent for HashChangeEvent {}
impl ConcreteEvent for HashChangeEvent {
    const EVENT_TYPE: &'static str = "hashchange";
}

impl HashChangeEvent {
    /// The previous URL from which the window was navigated.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent)
    // https://html.spec.whatwg.org/#the-hashchangeevent-interface:dom-hashchangeevent-oldurl
    #[inline]
    pub fn old_url( &self ) -> String {
        js!(
            return @{self.as_ref()}.oldURL;
        ).try_into().unwrap()
    }

    /// The new URL to which the window was navigated.
    ///
    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent)
    // https://html.spec.whatwg.org/#the-hashchangeevent-interface:dom-hashchangeevent-newurl
    #[inline]
    pub fn new_url( &self ) -> String {
        js!(
            return @{self.as_ref()}.newURL;
        ).try_into().unwrap()
    }
}

/// A `PopStateEvent` is dispatched to the window every time the active history entry changes
/// between two history entries for the same document. If the history entry being activated was
/// created by a call to `history.push_state()` or was affected by a call to
/// `history.replace_state()`, the `PopStateEvent`'s state property contains a copy of the history
/// entry's state object.
///
/// Note that just calling `history.push_state()` or `history.replace_state()` won't trigger a
/// `PopStateEvent`. The `PopStateEvent` is only triggered by doing a browser action such as a
/// clicking on the back button (or calling `history.back()`). And the event is only
/// triggered when the user navigates between two history entries for the same document.
///
/// Browsers tend to handle the `PopStateEvent` differently on page load. Chrome and Safari always
/// emit a `PopStateEvent` on page load, but Firefox doesn't.
///
/// [(Javascript docs)](https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent)
// https://html.spec.whatwg.org/#event-popstate
// https://html.spec.whatwg.org/#popstateevent
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "Event")] // TODO: Better type check.
#[reference(subclass_of(Event))]
pub struct PopStateEvent(Reference);

impl PopStateEvent {
    /// The state object associated to the new history entry, if that entry was created with
    /// push_state or affected by replace_state.
    ///
    /// Example usage:
    ///
    /// ```rust,ignore
    /// let state: Option<MyStruct> = event.state().try_into().ok();
    /// ```
    // https://html.spec.whatwg.org/#dom-popstateevent-state
    #[inline]
    pub fn state(&self) -> Value {
        js!(return @{self}.state;)
    }
}

impl IEvent for PopStateEvent {}

impl ConcreteEvent for PopStateEvent {
    const EVENT_TYPE: &'static str = "popstate";
}

#[cfg(all(test, feature = "web_test"))]
mod tests {
    use super::*;

    #[test]
    fn test_hash_change_event() {
        let event: HashChangeEvent = js!(
            return new HashChangeEvent(
                @{HashChangeEvent::EVENT_TYPE},
                {
                    oldURL: "http://test.com#foo",
                    newURL: "http://test.com#bar"
                }
            );
        ).try_into().unwrap();
        assert_eq!( event.event_type(), HashChangeEvent::EVENT_TYPE );
        assert_eq!( event.old_url(), "http://test.com#foo" );
        assert_eq!( event.new_url(), "http://test.com#bar" );
    }

    #[test]
    fn test_pop_state_event() {
        let event: PopStateEvent = js!(
            return new PopStateEvent(
                @{PopStateEvent::EVENT_TYPE},
                {
                    state: {
                        color: "tomato"
                    }
                }
            );
        ).try_into().unwrap();

        assert_eq!(event.event_type(), PopStateEvent::EVENT_TYPE);

        let state_value: Value = event.state();
        let state: ::std::collections::BTreeMap<String, Value> = state_value
            .as_object()
            .unwrap()
            .into();
        let mut expected = ::std::collections::BTreeMap::new();
        expected.insert("color".to_string(), "tomato".into());

        assert_eq!(state, expected);
    }
}
<|endoftext|>


<commit_before>use layers::{ARGB32Format, ContainerLayerKind, Image, ImageLayerKind, RGB24Format};
use layers::{TiledImageLayerKind};
use scene::Scene;

use geom::matrix::{Matrix4, ortho};
use opengles::gl2::{ARRAY_BUFFER, COLOR_BUFFER_BIT, COMPILE_STATUS};
use opengles::gl2::{FRAGMENT_SHADER, LINEAR, LINK_STATUS, NEAREST, NO_ERROR, REPEAT, RGB, RGBA,
                      BGRA};
use opengles::gl2::{STATIC_DRAW, TEXTURE_2D, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
use opengles::gl2::{TEXTURE_WRAP_S, TEXTURE_WRAP_T};
use opengles::gl2::{TRIANGLE_STRIP, UNPACK_ALIGNMENT, UNSIGNED_BYTE, VERTEX_SHADER, GLclampf};
use opengles::gl2::{GLenum, GLint, GLsizei, GLuint, attach_shader, bind_buffer, bind_texture};
use opengles::gl2::{buffer_data, create_program, clear, clear_color};
use opengles::gl2::{compile_shader, create_shader, draw_arrays, enable};
use opengles::gl2::{enable_vertex_attrib_array, gen_buffers, gen_textures};
use opengles::gl2::{get_attrib_location, get_error, get_program_iv};
use opengles::gl2::{get_shader_info_log, get_shader_iv};
use opengles::gl2::{get_uniform_location, link_program, pixel_store_i, shader_source};
use opengles::gl2::{tex_image_2d, tex_parameter_i, uniform_1i, uniform_matrix_4fv, use_program};
use opengles::gl2::{vertex_attrib_pointer_f32, viewport};

use io::println;
use libc::c_int;
use str::to_bytes;

pub fn FRAGMENT_SHADER_SOURCE() -> ~str {
    ~"
        #ifdef GLES2
            precision mediump float;
        #endif

        varying vec2 vTextureCoord;

        uniform sampler2D uSampler;

        void main(void) {
            gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
        }
    "
}

pub fn VERTEX_SHADER_SOURCE() -> ~str {
    ~"
        attribute vec3 aVertexPosition;
        attribute vec2 aTextureCoord;

        uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;

        varying vec2 vTextureCoord;

        void main(void) {
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
            vTextureCoord = aTextureCoord;
        }
    "
}

pub fn load_shader(source_string: ~str, shader_type: GLenum) -> GLuint {
    let shader_id = create_shader(shader_type);
    shader_source(shader_id, ~[ to_bytes(source_string) ]);
    compile_shader(shader_id);

    if get_error() != NO_ERROR {
        println(#fmt("error: %d", get_error() as int));
        fail ~"failed to compile shader";
    }

    if get_shader_iv(shader_id, COMPILE_STATUS) == (0 as GLint) {
        println(#fmt("shader info log: %s", get_shader_info_log(shader_id)));
        fail ~"failed to compile shader";
    }

    return shader_id;
}

pub struct RenderContext {
    program: GLuint,
    vertex_position_attr: c_int,
    texture_coord_attr: c_int,
    modelview_uniform: c_int,
    projection_uniform: c_int,
    sampler_uniform: c_int,
    vertex_buffer: GLuint,
    texture_coord_buffer: GLuint,
}

pub fn RenderContext(program: GLuint) -> RenderContext {
    let (vertex_buffer, texture_coord_buffer) = init_buffers();
    let rc = RenderContext {
        program : program,
        vertex_position_attr : get_attrib_location(program, ~"aVertexPosition"),
        texture_coord_attr : get_attrib_location(program, ~"aTextureCoord"),
        modelview_uniform : get_uniform_location(program, ~"uMVMatrix"),
        projection_uniform : get_uniform_location(program, ~"uPMatrix"),
        sampler_uniform : get_uniform_location(program, ~"uSampler"),
        vertex_buffer : vertex_buffer,
        texture_coord_buffer : texture_coord_buffer,
    };

    enable_vertex_attrib_array(rc.vertex_position_attr as GLuint);
    enable_vertex_attrib_array(rc.texture_coord_attr as GLuint);

    rc
}

pub fn init_render_context() -> RenderContext {
    let vertex_shader = load_shader(VERTEX_SHADER_SOURCE(), VERTEX_SHADER);
    let fragment_shader = load_shader(FRAGMENT_SHADER_SOURCE(), FRAGMENT_SHADER);

    let program = create_program();
    attach_shader(program, vertex_shader);
    attach_shader(program, fragment_shader);
    link_program(program);

    if get_program_iv(program, LINK_STATUS) == (0 as GLint) {
        fail ~"failed to initialize program";
    }

    use_program(program);

    enable(TEXTURE_2D);

    return RenderContext(program);
}

pub fn init_buffers() -> (GLuint, GLuint) {
    let triangle_vertex_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, triangle_vertex_buffer);

    let (_0, _1) = (0.0f32, 1.0f32);
    let vertices = ~[
        _0, _0, _0,
        _0, _1, _0,
        _1, _0, _0,
        _1, _1, _0
    ];

    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);

    let texture_coord_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, texture_coord_buffer);

    let vertices = ~[
        _0, _0,
        _0, _1,
        _1, _0,
        _1, _1
    ];

    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);

    return (triangle_vertex_buffer, texture_coord_buffer);
}

pub fn create_texture_for_image_if_necessary(image: @Image) {
    match image.texture {
        None => {}
        Some(_) => { return; /* Nothing to do. */ }
    }

    #debug("making texture");

    let texture = gen_textures(1 as GLsizei)[0];
    bind_texture(TEXTURE_2D, texture);

    tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_S, REPEAT as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_T, REPEAT as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR as GLint);

    pixel_store_i(UNPACK_ALIGNMENT, 1);

    fn borrow(a: &a/[u8]) -> &a/[u8] { a }

    match image.format {
      RGB24Format => {
        tex_image_2d(TEXTURE_2D, 0 as GLint, RGB as GLint, image.width as GLsizei,
                     image.height as GLsizei, 0 as GLint, RGB, UNSIGNED_BYTE,
                     Some(borrow(image.data)));
      }
      ARGB32Format => {
        tex_image_2d(TEXTURE_2D, 0 as GLint, RGBA as GLint, image.width as GLsizei,
                     image.height as GLsizei, 0 as GLint, BGRA, UNSIGNED_BYTE,
                     Some(borrow(image.data)));
      }
    }

    image.texture = Some(texture);
}

pub fn bind_and_render_quad(render_context: RenderContext, texture: GLuint) {
    bind_texture(TEXTURE_2D, texture);

    uniform_1i(render_context.sampler_uniform, 0);

    bind_buffer(ARRAY_BUFFER, render_context.vertex_buffer);
    vertex_attrib_pointer_f32(render_context.vertex_position_attr as GLuint, 3, false, 0, 0);

    bind_buffer(ARRAY_BUFFER, render_context.texture_coord_buffer);
    vertex_attrib_pointer_f32(render_context.texture_coord_attr as GLuint, 2, false, 0, 0);

    draw_arrays(TRIANGLE_STRIP, 0, 4);
}

// Layer rendering

pub trait Render {
    fn render(render_context: RenderContext);
}

impl @layers::ImageLayer : Render {
    fn render(render_context: RenderContext) {
        create_texture_for_image_if_necessary(self.image);

        uniform_matrix_4fv(render_context.modelview_uniform, false,
                           self.common.transform.to_array());

        bind_and_render_quad(render_context, option::get(&self.image.texture));
    }
}

impl @layers::TiledImageLayer : Render {
    fn render(render_context: RenderContext) {
        let tiles_down = self.tiles.len() / self.tiles_across;
        for self.tiles.eachi |i, tile| {
            create_texture_for_image_if_necessary(*tile);

            let x = ((i % self.tiles_across) as f32);
            let y = ((i / self.tiles_across) as f32);

            let transform = self.common.transform.scale(&(1.0f32 / (self.tiles_across as f32)),
                                                        &(1.0f32 / (tiles_down as f32)),
                                                        &1.0f32);
            let transform = transform.translate(&(x * 1.0f32), &(y * 1.0f32), &0.0f32);

            uniform_matrix_4fv(render_context.modelview_uniform, false, transform.to_array());

            bind_and_render_quad(render_context, option::get(&tile.texture));
        }
    }
}

pub fn render_scene(render_context: RenderContext, scene: &Scene) {
    // Set the viewport.
    viewport(0 as GLint, 0 as GLint, scene.size.width as GLsizei, scene.size.height as GLsizei);

    // Clear the screen.
    clear_color(0.0f32, 0.0f32, 1.0f32, 1.0f32);
    clear(COLOR_BUFFER_BIT);

    // Set the projection matrix.
    let projection_matrix = ortho(0.0f32, copy scene.size.width, copy scene.size.height, 0.0f32,
                                  -10.0f32, 10.0f32);
    uniform_matrix_4fv(render_context.projection_uniform, false, projection_matrix.to_array());

    match copy scene.root {
        ContainerLayerKind(*) => fail ~"container layers unsupported",
        ImageLayerKind(image_layer) => image_layer.render(render_context),
        TiledImageLayerKind(tiled_image_layer) => tiled_image_layer.render(render_context)
    }
}

<commit_msg>Use a more attractive background color<commit_after>use layers::{ARGB32Format, ContainerLayerKind, Image, ImageLayerKind, RGB24Format};
use layers::{TiledImageLayerKind};
use scene::Scene;

use geom::matrix::{Matrix4, ortho};
use opengles::gl2::{ARRAY_BUFFER, COLOR_BUFFER_BIT, COMPILE_STATUS};
use opengles::gl2::{FRAGMENT_SHADER, LINEAR, LINK_STATUS, NEAREST, NO_ERROR, REPEAT, RGB, RGBA,
                      BGRA};
use opengles::gl2::{STATIC_DRAW, TEXTURE_2D, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
use opengles::gl2::{TEXTURE_WRAP_S, TEXTURE_WRAP_T};
use opengles::gl2::{TRIANGLE_STRIP, UNPACK_ALIGNMENT, UNSIGNED_BYTE, VERTEX_SHADER, GLclampf};
use opengles::gl2::{GLenum, GLint, GLsizei, GLuint, attach_shader, bind_buffer, bind_texture};
use opengles::gl2::{buffer_data, create_program, clear, clear_color};
use opengles::gl2::{compile_shader, create_shader, draw_arrays, enable};
use opengles::gl2::{enable_vertex_attrib_array, gen_buffers, gen_textures};
use opengles::gl2::{get_attrib_location, get_error, get_program_iv};
use opengles::gl2::{get_shader_info_log, get_shader_iv};
use opengles::gl2::{get_uniform_location, link_program, pixel_store_i, shader_source};
use opengles::gl2::{tex_image_2d, tex_parameter_i, uniform_1i, uniform_matrix_4fv, use_program};
use opengles::gl2::{vertex_attrib_pointer_f32, viewport};

use io::println;
use libc::c_int;
use str::to_bytes;

pub fn FRAGMENT_SHADER_SOURCE() -> ~str {
    ~"
        #ifdef GLES2
            precision mediump float;
        #endif

        varying vec2 vTextureCoord;

        uniform sampler2D uSampler;

        void main(void) {
            gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
        }
    "
}

pub fn VERTEX_SHADER_SOURCE() -> ~str {
    ~"
        attribute vec3 aVertexPosition;
        attribute vec2 aTextureCoord;

        uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;

        varying vec2 vTextureCoord;

        void main(void) {
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
            vTextureCoord = aTextureCoord;
        }
    "
}

pub fn load_shader(source_string: ~str, shader_type: GLenum) -> GLuint {
    let shader_id = create_shader(shader_type);
    shader_source(shader_id, ~[ to_bytes(source_string) ]);
    compile_shader(shader_id);

    if get_error() != NO_ERROR {
        println(#fmt("error: %d", get_error() as int));
        fail ~"failed to compile shader";
    }

    if get_shader_iv(shader_id, COMPILE_STATUS) == (0 as GLint) {
        println(#fmt("shader info log: %s", get_shader_info_log(shader_id)));
        fail ~"failed to compile shader";
    }

    return shader_id;
}

pub struct RenderContext {
    program: GLuint,
    vertex_position_attr: c_int,
    texture_coord_attr: c_int,
    modelview_uniform: c_int,
    projection_uniform: c_int,
    sampler_uniform: c_int,
    vertex_buffer: GLuint,
    texture_coord_buffer: GLuint,
}

pub fn RenderContext(program: GLuint) -> RenderContext {
    let (vertex_buffer, texture_coord_buffer) = init_buffers();
    let rc = RenderContext {
        program : program,
        vertex_position_attr : get_attrib_location(program, ~"aVertexPosition"),
        texture_coord_attr : get_attrib_location(program, ~"aTextureCoord"),
        modelview_uniform : get_uniform_location(program, ~"uMVMatrix"),
        projection_uniform : get_uniform_location(program, ~"uPMatrix"),
        sampler_uniform : get_uniform_location(program, ~"uSampler"),
        vertex_buffer : vertex_buffer,
        texture_coord_buffer : texture_coord_buffer,
    };

    enable_vertex_attrib_array(rc.vertex_position_attr as GLuint);
    enable_vertex_attrib_array(rc.texture_coord_attr as GLuint);

    rc
}

pub fn init_render_context() -> RenderContext {
    let vertex_shader = load_shader(VERTEX_SHADER_SOURCE(), VERTEX_SHADER);
    let fragment_shader = load_shader(FRAGMENT_SHADER_SOURCE(), FRAGMENT_SHADER);

    let program = create_program();
    attach_shader(program, vertex_shader);
    attach_shader(program, fragment_shader);
    link_program(program);

    if get_program_iv(program, LINK_STATUS) == (0 as GLint) {
        fail ~"failed to initialize program";
    }

    use_program(program);

    enable(TEXTURE_2D);

    return RenderContext(program);
}

pub fn init_buffers() -> (GLuint, GLuint) {
    let triangle_vertex_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, triangle_vertex_buffer);

    let (_0, _1) = (0.0f32, 1.0f32);
    let vertices = ~[
        _0, _0, _0,
        _0, _1, _0,
        _1, _0, _0,
        _1, _1, _0
    ];

    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);

    let texture_coord_buffer = gen_buffers(1 as GLsizei)[0];
    bind_buffer(ARRAY_BUFFER, texture_coord_buffer);

    let vertices = ~[
        _0, _0,
        _0, _1,
        _1, _0,
        _1, _1
    ];

    buffer_data(ARRAY_BUFFER, vertices, STATIC_DRAW);

    return (triangle_vertex_buffer, texture_coord_buffer);
}

pub fn create_texture_for_image_if_necessary(image: @Image) {
    match image.texture {
        None => {}
        Some(_) => { return; /* Nothing to do. */ }
    }

    #debug("making texture");

    let texture = gen_textures(1 as GLsizei)[0];
    bind_texture(TEXTURE_2D, texture);

    tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_S, REPEAT as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_WRAP_T, REPEAT as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR as GLint);
    tex_parameter_i(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR as GLint);

    pixel_store_i(UNPACK_ALIGNMENT, 1);

    fn borrow(a: &a/[u8]) -> &a/[u8] { a }

    match image.format {
      RGB24Format => {
        tex_image_2d(TEXTURE_2D, 0 as GLint, RGB as GLint, image.width as GLsizei,
                     image.height as GLsizei, 0 as GLint, RGB, UNSIGNED_BYTE,
                     Some(borrow(image.data)));
      }
      ARGB32Format => {
        tex_image_2d(TEXTURE_2D, 0 as GLint, RGBA as GLint, image.width as GLsizei,
                     image.height as GLsizei, 0 as GLint, BGRA, UNSIGNED_BYTE,
                     Some(borrow(image.data)));
      }
    }

    image.texture = Some(texture);
}

pub fn bind_and_render_quad(render_context: RenderContext, texture: GLuint) {
    bind_texture(TEXTURE_2D, texture);

    uniform_1i(render_context.sampler_uniform, 0);

    bind_buffer(ARRAY_BUFFER, render_context.vertex_buffer);
    vertex_attrib_pointer_f32(render_context.vertex_position_attr as GLuint, 3, false, 0, 0);

    bind_buffer(ARRAY_BUFFER, render_context.texture_coord_buffer);
    vertex_attrib_pointer_f32(render_context.texture_coord_attr as GLuint, 2, false, 0, 0);

    draw_arrays(TRIANGLE_STRIP, 0, 4);
}

// Layer rendering

pub trait Render {
    fn render(render_context: RenderContext);
}

impl @layers::ImageLayer : Render {
    fn render(render_context: RenderContext) {
        create_texture_for_image_if_necessary(self.image);

        uniform_matrix_4fv(render_context.modelview_uniform, false,
                           self.common.transform.to_array());

        bind_and_render_quad(render_context, option::get(&self.image.texture));
    }
}

impl @layers::TiledImageLayer : Render {
    fn render(render_context: RenderContext) {
        let tiles_down = self.tiles.len() / self.tiles_across;
        for self.tiles.eachi |i, tile| {
            create_texture_for_image_if_necessary(*tile);

            let x = ((i % self.tiles_across) as f32);
            let y = ((i / self.tiles_across) as f32);

            let transform = self.common.transform.scale(&(1.0f32 / (self.tiles_across as f32)),
                                                        &(1.0f32 / (tiles_down as f32)),
                                                        &1.0f32);
            let transform = transform.translate(&(x * 1.0f32), &(y * 1.0f32), &0.0f32);

            uniform_matrix_4fv(render_context.modelview_uniform, false, transform.to_array());

            bind_and_render_quad(render_context, option::get(&tile.texture));
        }
    }
}

pub fn render_scene(render_context: RenderContext, scene: &Scene) {
    // Set the viewport.
    viewport(0 as GLint, 0 as GLint, scene.size.width as GLsizei, scene.size.height as GLsizei);

    // Clear the screen.
    clear_color(0.38f32, 0.36f32, 0.36f32, 1.0f32);
    clear(COLOR_BUFFER_BIT);

    // Set the projection matrix.
    let projection_matrix = ortho(0.0f32, copy scene.size.width, copy scene.size.height, 0.0f32,
                                  -10.0f32, 10.0f32);
    uniform_matrix_4fv(render_context.projection_uniform, false, projection_matrix.to_array());

    match copy scene.root {
        ContainerLayerKind(*) => fail ~"container layers unsupported",
        ImageLayerKind(image_layer) => image_layer.render(render_context),
        TiledImageLayerKind(tiled_image_layer) => tiled_image_layer.render(render_context)
    }
}

<|endoftext|>


<commit_before>extern crate futures;
extern crate rand;
extern crate tempfile;
extern crate tokio_fs;
extern crate tokio_io;
extern crate tokio_threadpool;

use tokio_fs::*;
use tokio_io::io;
use tokio_threadpool::*;

use futures::Future;
use futures::future::poll_fn;
use futures::sync::oneshot;
use rand::{thread_rng, Rng};
use tempfile::Builder as TmpBuilder;

use std::fs::File as StdFile;
use std::io::{Read, SeekFrom};

#[test]
fn read_write() {
    const NUM_CHARS: usize = 16 * 1_024;

    let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
    let file_path = dir.path().join("read_write.txt");

    let contents: Vec<u8> = thread_rng().gen_ascii_chars()
        .take(NUM_CHARS)
        .collect::<String>()
        .into();

    let pool = Builder::new()
        .pool_size(1)
        .build();

    let (tx, rx) = oneshot::channel();

    pool.spawn({
        let file_path = file_path.clone();
        let contents = contents.clone();

        File::create(file_path)
            .and_then(|file| file.metadata())
            .inspect(|&(_, ref metadata)| assert!(metadata.is_file()))
            .and_then(move |(file, _)| io::write_all(file, contents))
            .and_then(|(mut file, _)| {
                poll_fn(move || file.poll_sync_all())
            })
            .then(|res| {
                let _ = res.unwrap();
                tx.send(()).unwrap();
                Ok(())
            })
    });

    rx.wait().unwrap();

    let mut file = StdFile::open(&file_path).unwrap();

    let mut dst = vec![];
    file.read_to_end(&mut dst).unwrap();

    assert_eq!(dst, contents);

    let (tx, rx) = oneshot::channel();

    pool.spawn({
        File::open(file_path)
            .and_then(|file| io::read_to_end(file, vec![]))
            .then(move |res| {
                let (_, buf) = res.unwrap();
                assert_eq!(buf, contents);
                tx.send(()).unwrap();
                Ok(())
            })
    });

    rx.wait().unwrap();
}

#[test]
fn metadata() {
    let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
    let file_path = dir.path().join("metadata.txt");

    let pool = Builder::new().pool_size(1).build();

    let (tx, rx) = oneshot::channel();

    pool.spawn({
        let file_path = file_path.clone();
        let file_path2 = file_path.clone();
        let file_path3 = file_path.clone();

        tokio_fs::metadata(file_path)
            .then(|r| {
                let _ = r.err().unwrap();
                Ok(())
            })
            .and_then(|_| File::create(file_path2))
            .and_then(|_| tokio_fs::metadata(file_path3))
            .then(|r| {
                assert!(r.unwrap().is_file());
                tx.send(())
            })
    });

    rx.wait().unwrap();
}

#[test]
fn seek() {
    let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
    let file_path = dir.path().join("seek.txt");

    let pool = Builder::new().pool_size(1).build();

    let (tx, rx) = oneshot::channel();

    pool.spawn(
        OpenOptions::new()
            .create(true)
            .read(true)
            .write(true)
            .open(file_path)
            .and_then(|file| io::write_all(file, "Hello, world!"))
            .and_then(|(file, _)| file.seek(SeekFrom::End(-6)))
            .and_then(|(file, _)| io::read_exact(file, vec![0; 5]))
            .and_then(|(file, buf)| {
                assert_eq!(buf, b"world");
                file.seek(SeekFrom::Start(0))
            })
            .and_then(|(file, _)| io::read_exact(file, vec![0; 5]))
            .and_then(|(_, buf)| {
                assert_eq!(buf, b"Hello");
                Ok(())
            })
            .then(|r| {
                let _ = r.unwrap();
                tx.send(())
            }),
    );

    rx.wait().unwrap();
}
<commit_msg>fs: gen_ascii_chars has been deprecated (#735)<commit_after>extern crate futures;
extern crate rand;
extern crate tempfile;
extern crate tokio_fs;
extern crate tokio_io;
extern crate tokio_threadpool;

use tokio_fs::*;
use tokio_io::io;
use tokio_threadpool::*;

use futures::Future;
use futures::future::poll_fn;
use futures::sync::oneshot;
use rand::{thread_rng, Rng, distributions};
use tempfile::Builder as TmpBuilder;

use std::fs::File as StdFile;
use std::io::{Read, SeekFrom};

#[test]
fn read_write() {
    const NUM_CHARS: usize = 16 * 1_024;

    let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
    let file_path = dir.path().join("read_write.txt");

    let contents: Vec<u8> = thread_rng()
        .sample_iter(&distributions::Alphanumeric)
        .take(NUM_CHARS)
        .collect::<String>()
        .into();

    let pool = Builder::new()
        .pool_size(1)
        .build();

    let (tx, rx) = oneshot::channel();

    pool.spawn({
        let file_path = file_path.clone();
        let contents = contents.clone();

        File::create(file_path)
            .and_then(|file| file.metadata())
            .inspect(|&(_, ref metadata)| assert!(metadata.is_file()))
            .and_then(move |(file, _)| io::write_all(file, contents))
            .and_then(|(mut file, _)| {
                poll_fn(move || file.poll_sync_all())
            })
            .then(|res| {
                let _ = res.unwrap();
                tx.send(()).unwrap();
                Ok(())
            })
    });

    rx.wait().unwrap();

    let mut file = StdFile::open(&file_path).unwrap();

    let mut dst = vec![];
    file.read_to_end(&mut dst).unwrap();

    assert_eq!(dst, contents);

    let (tx, rx) = oneshot::channel();

    pool.spawn({
        File::open(file_path)
            .and_then(|file| io::read_to_end(file, vec![]))
            .then(move |res| {
                let (_, buf) = res.unwrap();
                assert_eq!(buf, contents);
                tx.send(()).unwrap();
                Ok(())
            })
    });

    rx.wait().unwrap();
}

#[test]
fn metadata() {
    let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
    let file_path = dir.path().join("metadata.txt");

    let pool = Builder::new().pool_size(1).build();

    let (tx, rx) = oneshot::channel();

    pool.spawn({
        let file_path = file_path.clone();
        let file_path2 = file_path.clone();
        let file_path3 = file_path.clone();

        tokio_fs::metadata(file_path)
            .then(|r| {
                let _ = r.err().unwrap();
                Ok(())
            })
            .and_then(|_| File::create(file_path2))
            .and_then(|_| tokio_fs::metadata(file_path3))
            .then(|r| {
                assert!(r.unwrap().is_file());
                tx.send(())
            })
    });

    rx.wait().unwrap();
}

#[test]
fn seek() {
    let dir = TmpBuilder::new().prefix("tokio-fs-tests").tempdir().unwrap();
    let file_path = dir.path().join("seek.txt");

    let pool = Builder::new().pool_size(1).build();

    let (tx, rx) = oneshot::channel();

    pool.spawn(
        OpenOptions::new()
            .create(true)
            .read(true)
            .write(true)
            .open(file_path)
            .and_then(|file| io::write_all(file, "Hello, world!"))
            .and_then(|(file, _)| file.seek(SeekFrom::End(-6)))
            .and_then(|(file, _)| io::read_exact(file, vec![0; 5]))
            .and_then(|(file, buf)| {
                assert_eq!(buf, b"world");
                file.seek(SeekFrom::Start(0))
            })
            .and_then(|(file, _)| io::read_exact(file, vec![0; 5]))
            .and_then(|(_, buf)| {
                assert_eq!(buf, b"Hello");
                Ok(())
            })
            .then(|r| {
                let _ = r.unwrap();
                tx.send(())
            }),
    );

    rx.wait().unwrap();
}
<|endoftext|>


<commit_before>use proc_macro2;
use proc_macro2::Span;
use syn;
use syn::fold::Fold;
use syn::spanned::Spanned;

use diagnostic_shim::*;
use meta::*;
use model::*;
use util::*;

pub fn derive(item: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Diagnostic> {
    let model = Model::from_item(&item)?;
    let tokens = MetaItem::all_with_name(&item.attrs, "belongs_to")
        .into_iter()
        .filter_map(
            |attr| match derive_belongs_to(&model, &item.generics, attr) {
                Ok(t) => Some(t),
                Err(e) => {
                    e.emit();
                    None
                }
            },
        );

    Ok(wrap_in_dummy_mod(
        model.dummy_mod_name("associations"),
        quote!(#(#tokens)*),
    ))
}

fn derive_belongs_to(
    model: &Model,
    generics: &syn::Generics,
    meta: MetaItem,
) -> Result<proc_macro2::TokenStream, Diagnostic> {
    let AssociationOptions {
        parent_struct,
        foreign_key,
    } = AssociationOptions::from_meta(meta)?;
    let (_, ty_generics, _) = generics.split_for_impl();

    let foreign_key_field = model.find_column(&foreign_key)?;
    let struct_name = &model.name;
    let foreign_key_access = foreign_key_field.name.access();
    let foreign_key_ty = inner_of_option_ty(&foreign_key_field.ty);
    let table_name = model.table_name();

    let mut generics = generics.clone();

    let parent_struct = ReplacePathLifetimes::new(|i, span| {
        let letter = char::from(b'b' + i as u8);
        let lifetime = syn::Lifetime::new(&format!("'__{}", letter), span);
        generics.params.push(parse_quote!(#lifetime));
        lifetime
    })
    .fold_type_path(parent_struct);

    // TODO: Remove this special casing as soon as we bump our minimal supported
    // rust version to >= 1.30.0 because this version will add
    // `impl<'a, T> From<&'a Option<T>> for Option<&'a T>` to the std-lib
    let (foreign_key_expr, foreign_key_ty) = if is_option_ty(&foreign_key_field.ty) {
        (
            quote!(self#foreign_key_access.as_ref()),
            quote!(#foreign_key_ty),
        )
    } else {
        generics.params.push(parse_quote!(__FK));
        {
            let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
            where_clause
                .predicates
                .push(parse_quote!(__FK: std::hash::Hash + std::cmp::Eq));
            where_clause.predicates.push(
                parse_quote!(for<'__a> &'__a #foreign_key_ty: std::convert::Into<::std::option::Option<&'__a __FK>>),
            );
            where_clause.predicates.push(
                parse_quote!(for<'__a> &'__a #parent_struct: diesel::associations::Identifiable<Id = &'__a __FK>),
            );
        }

        (
            quote!(std::convert::Into::into(&self#foreign_key_access)),
            quote!(__FK),
        )
    };

    let (impl_generics, _, where_clause) = generics.split_for_impl();

    Ok(quote! {
        impl #impl_generics diesel::associations::BelongsTo<#parent_struct>
            for #struct_name #ty_generics
        #where_clause
        {
            type ForeignKey = #foreign_key_ty;
            type ForeignKeyColumn = #table_name::#foreign_key;

            fn foreign_key(&self) -> std::option::Option<&Self::ForeignKey> {
                #foreign_key_expr
            }

            fn foreign_key_column() -> Self::ForeignKeyColumn {
                #table_name::#foreign_key
            }
        }
    })
}

struct AssociationOptions {
    parent_struct: syn::TypePath,
    foreign_key: syn::Ident,
}

impl AssociationOptions {
    fn from_meta(meta: MetaItem) -> Result<Self, Diagnostic> {
        let parent_struct = meta
            .nested()?
            .find(|m| m.path().is_ok() || m.name().is_ident("parent"))
            .ok_or_else(|| meta.span())
            .and_then(|m| {
                m.path()
                    .map(|i| parse_quote!(#i))
                    .or_else(|_| m.ty_value())
                    .map_err(|_| m.span())
            })
            .and_then(|ty| match ty {
                syn::Type::Path(ty_path) => Ok(ty_path),
                _ => Err(ty.span()),
            })
            .map_err(|span| {
                span.error("Expected a struct name")
                    .help("e.g. `#[belongs_to(User)]` or `#[belongs_to(parent = \"User<'_>\")]")
            })?;
        let foreign_key = {
            let parent_struct_name = parent_struct
                .path
                .segments
                .last()
                .expect("paths always have at least one segment");
            meta.nested_item("foreign_key")?
                .map(|i| i.ident_value())
                .unwrap_or_else(|| Ok(infer_foreign_key(&parent_struct_name.ident)))?
        };

        let unrecognized_options = meta
            .nested()?
            .skip(1)
            .filter(|n| !n.name().is_ident("foreign_key"));
        for ignored in unrecognized_options {
            ignored
                .span()
                .warning(format!(
                    "Unrecognized option {}",
                    path_to_string(&ignored.name())
                ))
                .emit();
        }

        Ok(Self {
            parent_struct,
            foreign_key,
        })
    }
}

fn infer_foreign_key(name: &syn::Ident) -> syn::Ident {
    let snake_case = camel_to_snake(&name.to_string());
    syn::Ident::new(&format!("{}_id", snake_case), name.span())
}

struct ReplacePathLifetimes<F> {
    count: usize,
    f: F,
}

impl<F> ReplacePathLifetimes<F> {
    fn new(f: F) -> Self {
        Self { count: 0, f }
    }
}

impl<F> Fold for ReplacePathLifetimes<F>
where
    F: FnMut(usize, Span) -> syn::Lifetime,
{
    fn fold_lifetime(&mut self, mut lt: syn::Lifetime) -> syn::Lifetime {
        if lt.ident == "_" {
            lt = (self.f)(self.count, lt.span());
            self.count += 1;
        }
        lt
    }
}
<commit_msg>Add a special warning when multiples parents are used in one belongs_to<commit_after>use proc_macro2;
use proc_macro2::Span;
use syn;
use syn::fold::Fold;
use syn::spanned::Spanned;

use diagnostic_shim::*;
use meta::*;
use model::*;
use util::*;

pub fn derive(item: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Diagnostic> {
    let model = Model::from_item(&item)?;
    let tokens = MetaItem::all_with_name(&item.attrs, "belongs_to")
        .into_iter()
        .filter_map(
            |attr| match derive_belongs_to(&model, &item.generics, attr) {
                Ok(t) => Some(t),
                Err(e) => {
                    e.emit();
                    None
                }
            },
        );

    Ok(wrap_in_dummy_mod(
        model.dummy_mod_name("associations"),
        quote!(#(#tokens)*),
    ))
}

fn derive_belongs_to(
    model: &Model,
    generics: &syn::Generics,
    meta: MetaItem,
) -> Result<proc_macro2::TokenStream, Diagnostic> {
    let AssociationOptions {
        parent_struct,
        foreign_key,
    } = AssociationOptions::from_meta(meta)?;
    let (_, ty_generics, _) = generics.split_for_impl();

    let foreign_key_field = model.find_column(&foreign_key)?;
    let struct_name = &model.name;
    let foreign_key_access = foreign_key_field.name.access();
    let foreign_key_ty = inner_of_option_ty(&foreign_key_field.ty);
    let table_name = model.table_name();

    let mut generics = generics.clone();

    let parent_struct = ReplacePathLifetimes::new(|i, span| {
        let letter = char::from(b'b' + i as u8);
        let lifetime = syn::Lifetime::new(&format!("'__{}", letter), span);
        generics.params.push(parse_quote!(#lifetime));
        lifetime
    })
    .fold_type_path(parent_struct);

    // TODO: Remove this special casing as soon as we bump our minimal supported
    // rust version to >= 1.30.0 because this version will add
    // `impl<'a, T> From<&'a Option<T>> for Option<&'a T>` to the std-lib
    let (foreign_key_expr, foreign_key_ty) = if is_option_ty(&foreign_key_field.ty) {
        (
            quote!(self#foreign_key_access.as_ref()),
            quote!(#foreign_key_ty),
        )
    } else {
        generics.params.push(parse_quote!(__FK));
        {
            let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
            where_clause
                .predicates
                .push(parse_quote!(__FK: std::hash::Hash + std::cmp::Eq));
            where_clause.predicates.push(
                parse_quote!(for<'__a> &'__a #foreign_key_ty: std::convert::Into<::std::option::Option<&'__a __FK>>),
            );
            where_clause.predicates.push(
                parse_quote!(for<'__a> &'__a #parent_struct: diesel::associations::Identifiable<Id = &'__a __FK>),
            );
        }

        (
            quote!(std::convert::Into::into(&self#foreign_key_access)),
            quote!(__FK),
        )
    };

    let (impl_generics, _, where_clause) = generics.split_for_impl();

    Ok(quote! {
        impl #impl_generics diesel::associations::BelongsTo<#parent_struct>
            for #struct_name #ty_generics
        #where_clause
        {
            type ForeignKey = #foreign_key_ty;
            type ForeignKeyColumn = #table_name::#foreign_key;

            fn foreign_key(&self) -> std::option::Option<&Self::ForeignKey> {
                #foreign_key_expr
            }

            fn foreign_key_column() -> Self::ForeignKeyColumn {
                #table_name::#foreign_key
            }
        }
    })
}

struct AssociationOptions {
    parent_struct: syn::TypePath,
    foreign_key: syn::Ident,
}

impl AssociationOptions {
    fn from_meta(meta: MetaItem) -> Result<Self, Diagnostic> {
        let parent_struct = meta
            .nested()?
            .find(|m| m.path().is_ok() || m.name().is_ident("parent"))
            .ok_or_else(|| meta.span())
            .and_then(|m| {
                m.path()
                    .map(|i| parse_quote!(#i))
                    .or_else(|_| m.ty_value())
                    .map_err(|_| m.span())
            })
            .and_then(|ty| match ty {
                syn::Type::Path(ty_path) => Ok(ty_path),
                _ => Err(ty.span()),
            })
            .map_err(|span| {
                span.error("Expected a struct name")
                    .help("e.g. `#[belongs_to(User)]` or `#[belongs_to(parent = \"User<'_>\")]")
            })?;
        let foreign_key = {
            let parent_struct_name = parent_struct
                .path
                .segments
                .last()
                .expect("paths always have at least one segment");
            meta.nested_item("foreign_key")?
                .map(|i| i.ident_value())
                .unwrap_or_else(|| Ok(infer_foreign_key(&parent_struct_name.ident)))?
        };

        let unrecognized_options = meta
            .nested()?
            .skip(1)
            .filter(|n| !n.name().is_ident("foreign_key"));
        for ignored in unrecognized_options {
            match ignored.word() {
                Ok(_) => {
                    ignored
                        .span()
                        .warning(format!(
                            "belongs_to takes a single parent. Change\n\
                            \tbelongs_to(Parent1, Parent2)\n\
                            to\n\
                            \tbelongs_to(Parent1)\n\
                            \tbelongs_to(Parent2)"
                        ))
                        .emit();
                },
                Err(_) => {
                    ignored
                        .span()
                        .warning(format!(
                            "Unrecognized option {}",
                            path_to_string(&ignored.name())
                        ))
                        .emit();
                }
            }
        }

        Ok(Self {
            parent_struct,
            foreign_key,
        })
    }
}

fn infer_foreign_key(name: &syn::Ident) -> syn::Ident {
    let snake_case = camel_to_snake(&name.to_string());
    syn::Ident::new(&format!("{}_id", snake_case), name.span())
}

struct ReplacePathLifetimes<F> {
    count: usize,
    f: F,
}

impl<F> ReplacePathLifetimes<F> {
    fn new(f: F) -> Self {
        Self { count: 0, f }
    }
}

impl<F> Fold for ReplacePathLifetimes<F>
where
    F: FnMut(usize, Span) -> syn::Lifetime,
{
    fn fold_lifetime(&mut self, mut lt: syn::Lifetime) -> syn::Lifetime {
        if lt.ident == "_" {
            lt = (self.f)(self.count, lt.span());
            self.count += 1;
        }
        lt
    }
}
<|endoftext|>


<commit_before>#![feature(test)]

#[macro_use]
extern crate frunk_core;
extern crate test;

use frunk_core::hlist::*;
use test::Bencher;

#[bench]
fn creating_hlist(b: &mut Bencher) {
    b.iter(|| hlist![1, 2, 3.3f32, "hi2", true])
}

#[bench]
fn creating_tuple2(b: &mut Bencher) {
    b.iter(|| (1, (2, (3.3f32, ("hi2", true)))))
}

#[bench]
fn hlist_into_tuple2(b: &mut Bencher) {
    let h = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| h.into_tuple2())
}

#[bench]
fn hlist_into_tuple2_match(b: &mut Bencher) {
    let h = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| {
        let (a, (b, (c, (d, e)))) = h.into_tuple2();
        (a, b, c, d, e)
    })
}

#[bench]
fn hlist_into_hlist_pat_match(b: &mut Bencher) {
    let h = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| {
        let hlist_pat!(a, b, c, d, e) = h;
        (a, b, c, d, e)
    })
}

#[bench]
fn hlist_append(b: &mut Bencher) {
    let h1 = hlist![1, 2, 3.3f32, "hi2", true];
    let h2 = hlist![true, "blue", "varcity"];
    b.iter(|| h1 + h2)
}

#[bench]
fn hlist_mapping_consuming(b: &mut Bencher) {
    let h1 = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| {
        h1.map(hlist![
            |i| i + 1,
            |i| i + 2,
            |i| i + 3f32,
            |s| s,
            |b: bool| !b,
        ]);
    });
}

#[bench]
fn hlist_mapping_non_consuming(b: &mut Bencher) {
    let h1 = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| {
        h1.to_ref().map(hlist![
            |&i| i + 1,
            |&i| i + 2,
            |&i| i + 3f32,
            |&s| s,
            |&b: &bool| !b,
        ]);
    });
}
<commit_msg>Fix benchmark<commit_after>#![feature(test)]

#[macro_use]
extern crate frunk_core;
extern crate test;

use frunk_core::hlist::*;
use test::Bencher;

#[bench]
fn creating_hlist(b: &mut Bencher) {
    b.iter(|| hlist![1, 2, 3.3f32, "hi2", true])
}

#[bench]
fn creating_tuple2(b: &mut Bencher) {
    b.iter(|| (1, (2, (3.3f32, ("hi2", true)))))
}

#[bench]
fn hlist_into_tuple2(b: &mut Bencher) {
    let h = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| h.into_tuple2())
}

#[bench]
fn hlist_into_tuple2_match(b: &mut Bencher) {
    let h = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| {
        let (a, (b, (c, (d, e)))) = h.into_tuple2();
        (a, b, c, d, e)
    })
}

#[bench]
fn hlist_into_hlist_pat_match(b: &mut Bencher) {
    let h = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| {
        let hlist_pat!(a, b, c, d, e) = h;
        (a, b, c, d, e)
    })
}

#[bench]
fn hlist_append(b: &mut Bencher) {
    let h1 = hlist![1, 2, 3.3f32, "hi2", true];
    let h2 = hlist![true, "blue", "varcity"];
    b.iter(|| h1 + h2)
}

#[bench]
fn hlist_mapping_consuming(b: &mut Bencher) {
    let h1 = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| {
        h1.map(hlist![
            |i| i + 1,
            |i| i + 2,
            |i| i + 3f32,
            |s| s,
            |b: bool| !b,
        ])
    });
}

#[bench]
fn hlist_mapping_non_consuming(b: &mut Bencher) {
    let h1 = hlist![1, 2, 3.3f32, "hi2", true];
    b.iter(|| {
        h1.to_ref().map(hlist![
            |&i| i + 1,
            |&i| i + 2,
            |&i| i + 3f32,
            |&s| s,
            |&b: &bool| !b,
        ])
    });
}
<|endoftext|>


<commit_before>/*!
A Rust interface for Objective-C blocks.

For more information on the specifics of the block implementation, see
Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html

# Invoking blocks

The `Block` struct is used for invoking blocks from Objective-C. For example,
consider this Objective-C function:

``` objc
int32_t sum(int32_t (^block)(int32_t, int32_t)) {
    return block(5, 8);
}
```

We could write it in Rust as the following:

```
use objc::block::Block;

fn sum(block: &mut Block<(i32, i32), i32>) -> i32 {
    block.call((5, 8))
}
```

Note the extra parentheses in the `call` method, since the arguments must be
passed as a tuple.

# Creating blocks

Creating a block to pass to Objective-C can be done with the `ConcreteBlock`
struct. For example, to create a block that adds two `i32`s, we could write:

```
use objc::block::ConcreteBlock;

let block = ConcreteBlock::new(|a: i32, b: i32| a + b);
let mut block = block.copy();
assert!(block.call((5, 8)) == 13);
```

It is important to copy your block to the heap (with the `copy` method) before
passing it to Objective-C; this is because our `ConcreteBlock` is only meant
to be copied once, and we can enforce this in Rust, but if Objective-C code
were to copy it twice we could have a double free.
*/

use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::{Class, Object};
use {EncodePtr, Id, Message};

#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

/// Types that may be used as the arguments to an Objective-C block.
pub trait BlockArguments {
    /// Calls the given `Block` with self as the arguments.
    fn call_block<R>(self, block: &mut Block<Self, R>) -> R;
}

macro_rules! block_args_impl {
    ($($a:ident : $t:ident),*) => (
        impl<$($t),*> BlockArguments for ($($t,)*) {
            fn call_block<R>(self, block: &mut Block<Self, R>) -> R {
                let invoke: unsafe extern fn(*mut Block<Self, R> $(, $t)*) -> R = unsafe {
                    mem::transmute(block.invoke)
                };
                let ($($a,)*) = self;
                unsafe {
                    invoke(block $(, $a)*)
                }
            }
        }
    );
}

block_args_impl!();
block_args_impl!(a: A);
block_args_impl!(a: A, b: B);
block_args_impl!(a: A, b: B, c: C);
block_args_impl!(a: A, b: B, c: C, d: D);
block_args_impl!(a: A, b: B, c: C, d: D, e: E);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block that takes arguments of `A` when called and
/// returns a value of `R`.
#[repr(C)]
pub struct Block<A, R> {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    invoke: unsafe extern fn(*mut Block<A, R>, ...) -> R,
}

// TODO: impl FnMut when it's possible
impl<A: BlockArguments, R> Block<A, R> where A: BlockArguments {
    /// Call self with the given arguments.
    pub fn call(&mut self, args: A) -> R {
        args.call_block(self)
    }
}

unsafe impl<A, R> Message for Block<A, R> { }

impl<A, R> EncodePtr for Block<A, R> {
    fn ptr_code() -> &'static str { "@?" }
}

pub trait IntoConcreteBlock<A, R> where A: BlockArguments {
    fn into_concrete_block(self) -> ConcreteBlock<A, R, Self>;
}

macro_rules! concrete_block_impl {
    ($f:ident) => (
        concrete_block_impl!($f,);
    );
    ($f:ident, $($a:ident : $t:ident),*) => (
        impl<$($t,)* R, X> IntoConcreteBlock<($($t,)*), R> for X
                where X: Fn($($t,)*) -> R {
            fn into_concrete_block(self) -> ConcreteBlock<($($t,)*), R, X> {
                unsafe extern fn $f<$($t,)* R, X>(
                        block_ptr: *mut ConcreteBlock<($($t,)*), R, X>
                        $(, $a: $t)*) -> R
                        where X: Fn($($t,)*) -> R {
                    let block = &*block_ptr;
                    (block.closure)($($a),*)
                }

                unsafe {
                    ConcreteBlock::with_invoke(
                        mem::transmute($f::<$($t,)* R, X>), self)
                }
            }
        }
    );
}

concrete_block_impl!(concrete_block_invoke_args0);
concrete_block_impl!(concrete_block_invoke_args1, a: A);
concrete_block_impl!(concrete_block_invoke_args2, a: A, b: B);
concrete_block_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
concrete_block_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
concrete_block_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
concrete_block_impl!(concrete_block_invoke_args6, a: A, b: B, c: C, d: D, e: E, f: F);
concrete_block_impl!(concrete_block_invoke_args7, a: A, b: B, c: C, d: D, e: E, f: F, g: G);
concrete_block_impl!(concrete_block_invoke_args8, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
concrete_block_impl!(concrete_block_invoke_args9, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
concrete_block_impl!(concrete_block_invoke_args10, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
concrete_block_impl!(concrete_block_invoke_args11, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
concrete_block_impl!(concrete_block_invoke_args12, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block whose size is known at compile time and may be
/// constructed on the stack.
#[repr(C)]
pub struct ConcreteBlock<A, R, F> {
    base: Block<A, R>,
    descriptor: Box<BlockDescriptor<ConcreteBlock<A, R, F>>>,
    closure: F,
}

impl<A, R, F> ConcreteBlock<A, R, F>
        where A: BlockArguments, F: IntoConcreteBlock<A, R> {
    /// Constructs a `ConcreteBlock` with the given closure.
    /// When the block is called, it will return the value that results from
    /// calling the closure.
    pub fn new(closure: F) -> Self {
        closure.into_concrete_block()
    }
}

impl<A, R, F> ConcreteBlock<A, R, F> {
    /// Constructs a `ConcreteBlock` with the given invoke function and closure.
    /// Unsafe because the caller must ensure the invoke function takes the
    /// correct arguments.
    unsafe fn with_invoke(invoke: unsafe extern fn(*mut Self, ...) -> R,
            closure: F) -> Self {
        ConcreteBlock {
            base: Block {
                isa: &_NSConcreteStackBlock,
                // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
                flags: 1 << 25,
                _reserved: 0,
                invoke: mem::transmute(invoke),
            },
            descriptor: Box::new(BlockDescriptor::new()),
            closure: closure,
        }
    }

    /// Copy self onto the heap.
    pub fn copy(self) -> Id<Block<A, R>> {
        unsafe {
            // The copy method is declared as returning an object pointer.
            let block: *mut Object = msg_send![&self.base, copy];
            let block = block as *mut Block<A, R>;
            // At this point, our copy helper has been run so the block will
            // be moved to the heap and we can forget the original block
            // because the heap block will drop in our dispose helper.
            mem::forget(self);
            Id::from_retained_ptr(block)
        }
    }
}

impl<A, R, F> Clone for ConcreteBlock<A, R, F> where F: Clone {
    fn clone(&self) -> Self {
        unsafe {
            ConcreteBlock::with_invoke(mem::transmute(self.invoke),
                self.closure.clone())
        }
    }
}

impl<A, R, F> Deref for ConcreteBlock<A, R, F> {
    type Target = Block<A, R>;

    fn deref(&self) -> &Block<A, R> {
        &self.base
    }
}

impl<A, R, F> DerefMut for ConcreteBlock<A, R, F> {
    fn deref_mut(&mut self) -> &mut Block<A, R> {
        &mut self.base
    }
}

unsafe extern fn block_context_dispose<B>(block: &mut B) {
    // Read the block onto the stack and let it drop
    ptr::read(block);
}

unsafe extern fn block_context_copy<B>(_dst: &mut B, _src: &B) {
    // The runtime memmoves the src block into the dst block, nothing to do
}

#[repr(C)]
struct BlockDescriptor<B> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut B, &B),
    dispose_helper: unsafe extern fn(&mut B),
}

impl<B> BlockDescriptor<B> {
    fn new() -> BlockDescriptor<B> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<B>() as c_ulong,
            copy_helper: block_context_copy::<B>,
            dispose_helper: block_context_dispose::<B>,
        }
    }
}

#[cfg(test)]
mod tests {
    use Id;
    use objc_test_utils;
    use super::{Block, ConcreteBlock};

    fn get_int_block_with(i: i32) -> Id<Block<(), i32>> {
        unsafe {
            let ptr = objc_test_utils::get_int_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn get_add_block_with(i: i32) -> Id<Block<(i32,), i32>> {
        unsafe {
            let ptr = objc_test_utils::get_add_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn invoke_int_block(block: &mut Block<(), i32>) -> i32 {
        let ptr = block as *mut _;
        unsafe {
            objc_test_utils::invoke_int_block(ptr as *mut _)
        }
    }

    fn invoke_add_block(block: &mut Block<(i32,), i32>, a: i32) -> i32 {
        let ptr = block as *mut _;
        unsafe {
            objc_test_utils::invoke_add_block(ptr as *mut _, a)
        }
    }

    #[test]
    fn test_call_block() {
        let mut block = get_int_block_with(13);
        assert!(block.call(()) == 13);
    }

    #[test]
    fn test_call_block_args() {
        let mut block = get_add_block_with(13);
        assert!(block.call((2,)) == 15);
    }

    #[test]
    fn test_create_block() {
        let mut block = ConcreteBlock::new(|| 13);
        let result = invoke_int_block(&mut block);
        assert!(result == 13);
    }

    #[test]
    fn test_create_block_args() {
        let mut block = ConcreteBlock::new(|a: i32| a + 5);
        let result = invoke_add_block(&mut block, 6);
        assert!(result == 11);
    }

    #[test]
    fn test_concrete_block_copy() {
        let s = "Hello!".to_string();
        let expected_len = s.len() as i32;
        let mut block = ConcreteBlock::new(move || s.len() as i32);
        assert!(invoke_int_block(&mut block) == expected_len);

        let mut copied = block.copy();
        assert!(invoke_int_block(&mut copied) == expected_len);
    }
}
<commit_msg>Added failing Block test.<commit_after>/*!
A Rust interface for Objective-C blocks.

For more information on the specifics of the block implementation, see
Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html

# Invoking blocks

The `Block` struct is used for invoking blocks from Objective-C. For example,
consider this Objective-C function:

``` objc
int32_t sum(int32_t (^block)(int32_t, int32_t)) {
    return block(5, 8);
}
```

We could write it in Rust as the following:

```
use objc::block::Block;

fn sum(block: &mut Block<(i32, i32), i32>) -> i32 {
    block.call((5, 8))
}
```

Note the extra parentheses in the `call` method, since the arguments must be
passed as a tuple.

# Creating blocks

Creating a block to pass to Objective-C can be done with the `ConcreteBlock`
struct. For example, to create a block that adds two `i32`s, we could write:

```
use objc::block::ConcreteBlock;

let block = ConcreteBlock::new(|a: i32, b: i32| a + b);
let mut block = block.copy();
assert!(block.call((5, 8)) == 13);
```

It is important to copy your block to the heap (with the `copy` method) before
passing it to Objective-C; this is because our `ConcreteBlock` is only meant
to be copied once, and we can enforce this in Rust, but if Objective-C code
were to copy it twice we could have a double free.
*/

use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::{Class, Object};
use {EncodePtr, Id, Message};

#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

/// Types that may be used as the arguments to an Objective-C block.
pub trait BlockArguments {
    /// Calls the given `Block` with self as the arguments.
    fn call_block<R>(self, block: &mut Block<Self, R>) -> R;
}

macro_rules! block_args_impl {
    ($($a:ident : $t:ident),*) => (
        impl<$($t),*> BlockArguments for ($($t,)*) {
            fn call_block<R>(self, block: &mut Block<Self, R>) -> R {
                let invoke: unsafe extern fn(*mut Block<Self, R> $(, $t)*) -> R = unsafe {
                    mem::transmute(block.invoke)
                };
                let ($($a,)*) = self;
                unsafe {
                    invoke(block $(, $a)*)
                }
            }
        }
    );
}

block_args_impl!();
block_args_impl!(a: A);
block_args_impl!(a: A, b: B);
block_args_impl!(a: A, b: B, c: C);
block_args_impl!(a: A, b: B, c: C, d: D);
block_args_impl!(a: A, b: B, c: C, d: D, e: E);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block that takes arguments of `A` when called and
/// returns a value of `R`.
#[repr(C)]
pub struct Block<A, R> {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    invoke: unsafe extern fn(*mut Block<A, R>, ...) -> R,
}

// TODO: impl FnMut when it's possible
impl<A: BlockArguments, R> Block<A, R> where A: BlockArguments {
    /// Call self with the given arguments.
    pub fn call(&mut self, args: A) -> R {
        args.call_block(self)
    }
}

unsafe impl<A, R> Message for Block<A, R> { }

impl<A, R> EncodePtr for Block<A, R> {
    fn ptr_code() -> &'static str { "@?" }
}

pub trait IntoConcreteBlock<A, R> where A: BlockArguments {
    fn into_concrete_block(self) -> ConcreteBlock<A, R, Self>;
}

macro_rules! concrete_block_impl {
    ($f:ident) => (
        concrete_block_impl!($f,);
    );
    ($f:ident, $($a:ident : $t:ident),*) => (
        impl<$($t,)* R, X> IntoConcreteBlock<($($t,)*), R> for X
                where X: Fn($($t,)*) -> R {
            fn into_concrete_block(self) -> ConcreteBlock<($($t,)*), R, X> {
                unsafe extern fn $f<$($t,)* R, X>(
                        block_ptr: *mut ConcreteBlock<($($t,)*), R, X>
                        $(, $a: $t)*) -> R
                        where X: Fn($($t,)*) -> R {
                    let block = &*block_ptr;
                    (block.closure)($($a),*)
                }

                unsafe {
                    ConcreteBlock::with_invoke(
                        mem::transmute($f::<$($t,)* R, X>), self)
                }
            }
        }
    );
}

concrete_block_impl!(concrete_block_invoke_args0);
concrete_block_impl!(concrete_block_invoke_args1, a: A);
concrete_block_impl!(concrete_block_invoke_args2, a: A, b: B);
concrete_block_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
concrete_block_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
concrete_block_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
concrete_block_impl!(concrete_block_invoke_args6, a: A, b: B, c: C, d: D, e: E, f: F);
concrete_block_impl!(concrete_block_invoke_args7, a: A, b: B, c: C, d: D, e: E, f: F, g: G);
concrete_block_impl!(concrete_block_invoke_args8, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
concrete_block_impl!(concrete_block_invoke_args9, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
concrete_block_impl!(concrete_block_invoke_args10, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
concrete_block_impl!(concrete_block_invoke_args11, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
concrete_block_impl!(concrete_block_invoke_args12, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block whose size is known at compile time and may be
/// constructed on the stack.
#[repr(C)]
pub struct ConcreteBlock<A, R, F> {
    base: Block<A, R>,
    descriptor: Box<BlockDescriptor<ConcreteBlock<A, R, F>>>,
    closure: F,
}

impl<A, R, F> ConcreteBlock<A, R, F>
        where A: BlockArguments, F: IntoConcreteBlock<A, R> {
    /// Constructs a `ConcreteBlock` with the given closure.
    /// When the block is called, it will return the value that results from
    /// calling the closure.
    pub fn new(closure: F) -> Self {
        closure.into_concrete_block()
    }
}

impl<A, R, F> ConcreteBlock<A, R, F> {
    /// Constructs a `ConcreteBlock` with the given invoke function and closure.
    /// Unsafe because the caller must ensure the invoke function takes the
    /// correct arguments.
    unsafe fn with_invoke(invoke: unsafe extern fn(*mut Self, ...) -> R,
            closure: F) -> Self {
        ConcreteBlock {
            base: Block {
                isa: &_NSConcreteStackBlock,
                // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
                flags: 1 << 25,
                _reserved: 0,
                invoke: mem::transmute(invoke),
            },
            descriptor: Box::new(BlockDescriptor::new()),
            closure: closure,
        }
    }

    /// Copy self onto the heap.
    pub fn copy(self) -> Id<Block<A, R>> {
        unsafe {
            // The copy method is declared as returning an object pointer.
            let block: *mut Object = msg_send![&self.base, copy];
            let block = block as *mut Block<A, R>;
            // At this point, our copy helper has been run so the block will
            // be moved to the heap and we can forget the original block
            // because the heap block will drop in our dispose helper.
            mem::forget(self);
            Id::from_retained_ptr(block)
        }
    }
}

impl<A, R, F> Clone for ConcreteBlock<A, R, F> where F: Clone {
    fn clone(&self) -> Self {
        unsafe {
            ConcreteBlock::with_invoke(mem::transmute(self.invoke),
                self.closure.clone())
        }
    }
}

impl<A, R, F> Deref for ConcreteBlock<A, R, F> {
    type Target = Block<A, R>;

    fn deref(&self) -> &Block<A, R> {
        &self.base
    }
}

impl<A, R, F> DerefMut for ConcreteBlock<A, R, F> {
    fn deref_mut(&mut self) -> &mut Block<A, R> {
        &mut self.base
    }
}

unsafe extern fn block_context_dispose<B>(block: &mut B) {
    // Read the block onto the stack and let it drop
    ptr::read(block);
}

unsafe extern fn block_context_copy<B>(_dst: &mut B, _src: &B) {
    // The runtime memmoves the src block into the dst block, nothing to do
}

#[repr(C)]
struct BlockDescriptor<B> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut B, &B),
    dispose_helper: unsafe extern fn(&mut B),
}

impl<B> BlockDescriptor<B> {
    fn new() -> BlockDescriptor<B> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<B>() as c_ulong,
            copy_helper: block_context_copy::<B>,
            dispose_helper: block_context_dispose::<B>,
        }
    }
}

#[cfg(test)]
mod tests {
    use Id;
    use objc_test_utils;
    use super::{Block, ConcreteBlock};

    fn get_int_block_with(i: i32) -> Id<Block<(), i32>> {
        unsafe {
            let ptr = objc_test_utils::get_int_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn get_add_block_with(i: i32) -> Id<Block<(i32,), i32>> {
        unsafe {
            let ptr = objc_test_utils::get_add_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn invoke_int_block(block: &mut Block<(), i32>) -> i32 {
        let ptr = block as *mut _;
        unsafe {
            objc_test_utils::invoke_int_block(ptr as *mut _)
        }
    }

    fn invoke_add_block(block: &mut Block<(i32,), i32>, a: i32) -> i32 {
        let ptr = block as *mut _;
        unsafe {
            objc_test_utils::invoke_add_block(ptr as *mut _, a)
        }
    }

    #[test]
    fn test_call_block() {
        let mut block = get_int_block_with(13);
        assert!(block.call(()) == 13);
    }

    #[test]
    fn test_call_block_args() {
        let mut block = get_add_block_with(13);
        assert!(block.call((2,)) == 15);
    }

    #[test]
    fn test_create_block() {
        let mut block = ConcreteBlock::new(|| 13);
        let result = invoke_int_block(&mut block);
        assert!(result == 13);
    }

    #[test]
    fn test_create_block_args() {
        let mut block = ConcreteBlock::new(|a: i32| a + 5);
        let result = invoke_add_block(&mut block, 6);
        assert!(result == 11);
    }

    #[test]
    fn test_concrete_block_copy() {
        let s = "Hello!".to_string();
        let expected_len = s.len() as i32;
        let mut block = ConcreteBlock::new(move || s.len() as i32);
        assert!(invoke_int_block(&mut block) == expected_len);

        let mut copied = block.copy();
        assert!(invoke_int_block(&mut copied) == expected_len);
    }

    #[test]
    fn test_concrete_block_stack_copy() {
        fn make_block() -> Id<Block<(), i32>> {
            let x = 7;
            let block = ConcreteBlock::new(|| x);
            block.copy()
        }

        let mut block = make_block();
        assert!(invoke_int_block(&mut block) == 7);
    }
}
<|endoftext|>