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

//! The task that handles all rendering/painting.

use buffer_map::BufferMap;
use display_list::optimizer::DisplayListOptimizer;
use display_list::DisplayList;
use font_context::FontContext;
use render_context::RenderContext;

use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
use layers;
use native;
use rustrt::task;
use rustrt::task::TaskOpts;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerId};
use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderState, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
use servo_msg::constellation_msg::{RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::geometry;
use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time;
use std::comm::{Receiver, Sender, channel};
use sync::Arc;
use font_cache_task::FontCacheTask;

/// Information about a layer that layout sends to the painting task.
pub struct RenderLayer {
    /// A per-pipeline ID describing this layer that should be stable across reflows.
    pub id: LayerId,
    /// The display list describing the contents of this layer.
    pub display_list: Arc<DisplayList>,
    /// The position of the layer in pixels.
    pub position: Rect<uint>,
    /// The color of the background in this layer. Used for unrendered content.
    pub background_color: Color,
    /// The scrolling policy of this layer.
    pub scroll_policy: ScrollPolicy,
}

pub struct ReRenderRequest {
    pub buffer_requests: Vec<BufferRequest>,
    pub scale: f32,
    pub layer_id: LayerId,
    pub epoch: Epoch,
}

pub enum Msg {
    RenderMsg(SmallVec1<RenderLayer>),
    ReRenderMsg(Vec<ReRenderRequest>),
    UnusedBufferMsg(Vec<Box<LayerBuffer>>),
    PaintPermissionGranted,
    PaintPermissionRevoked,
    ExitMsg(Option<Sender<()>>),
}

#[deriving(Clone)]
pub struct RenderChan(Sender<Msg>);

impl RenderChan {
    pub fn new() -> (Receiver<Msg>, RenderChan) {
        let (chan, port) = channel();
        (port, RenderChan(chan))
    }

    pub fn send(&self, msg: Msg) {
        let &RenderChan(ref chan) = self;
        assert!(chan.send_opt(msg).is_ok(), "RenderChan.send: render port closed")
    }

    pub fn send_opt(&self, msg: Msg) -> Result<(), Msg> {
        let &RenderChan(ref chan) = self;
        chan.send_opt(msg)
    }
}

/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
pub enum GraphicsContext {
    CpuGraphicsContext,
    GpuGraphicsContext,
}

pub struct RenderTask<C> {
    id: PipelineId,
    port: Receiver<Msg>,
    compositor: C,
    constellation_chan: ConstellationChan,
    font_ctx: Box<FontContext>,
    opts: Opts,

    /// A channel to the time profiler.
    time_profiler_chan: TimeProfilerChan,

    /// The graphics context to use.
    graphics_context: GraphicsContext,

    /// The native graphics context.
    native_graphics_context: Option<NativePaintingGraphicsContext>,

    /// The layers to be rendered.
    render_layers: SmallVec1<RenderLayer>,

    /// Permission to send paint messages to the compositor
    paint_permission: bool,

    /// A counter for epoch messages
    epoch: Epoch,

    /// A data structure to store unused LayerBuffers
    buffer_map: BufferMap<Box<LayerBuffer>>,
}

// If we implement this as a function, we get borrowck errors from borrowing
// the whole RenderTask struct.
macro_rules! native_graphics_context(
    ($task:expr) => (
        $task.native_graphics_context.as_ref().expect("Need a graphics context to do rendering")
    )
)

fn initialize_layers<C:RenderListener>(
                     compositor: &mut C,
                     pipeline_id: PipelineId,
                     epoch: Epoch,
                     render_layers: &[RenderLayer]) {
    let metadata = render_layers.iter().map(|render_layer| {
        LayerMetadata {
            id: render_layer.id,
            position: render_layer.position,
            background_color: render_layer.background_color,
            scroll_policy: render_layer.scroll_policy,
        }
    }).collect();
    compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
}

impl<C:RenderListener + Send> RenderTask<C> {
    pub fn create(id: PipelineId,
                  port: Receiver<Msg>,
                  compositor: C,
                  constellation_chan: ConstellationChan,
                  font_cache_task: FontCacheTask,
                  failure_msg: Failure,
                  opts: Opts,
                  time_profiler_chan: TimeProfilerChan,
                  shutdown_chan: Sender<()>) {

        let ConstellationChan(c) = constellation_chan.clone();
        let fc = font_cache_task.clone();

        let mut task_opts = TaskOpts::new();
        task_opts.name = Some("RenderTask".into_maybe_owned());
        task_opts.on_exit = Some(proc(result: task::Result) {
            match result {
                Ok(()) => {},
                Err(..) => {
                    c.send(FailureMsg(failure_msg));
                }
            }
        });
        native::task::spawn_opts(task_opts, proc() {
            { // Ensures RenderTask and graphics context are destroyed before shutdown msg
                let native_graphics_context = compositor.get_graphics_metadata().map(
                    |md| NativePaintingGraphicsContext::from_metadata(&md));
                let cpu_painting = opts.cpu_painting;

                // FIXME: rust/#5967
                let mut render_task = RenderTask {
                    id: id,
                    port: port,
                    compositor: compositor,
                    constellation_chan: constellation_chan,
                    font_ctx: box FontContext::new(fc.clone()),
                    opts: opts,
                    time_profiler_chan: time_profiler_chan,

                    graphics_context: if cpu_painting {
                        CpuGraphicsContext
                    } else {
                        GpuGraphicsContext
                    },

                    native_graphics_context: native_graphics_context,

                    render_layers: SmallVec1::new(),

                    paint_permission: false,
                    epoch: Epoch(0),
                    buffer_map: BufferMap::new(10000000),
                };

                render_task.start();

                // Destroy all the buffers.
                match render_task.native_graphics_context.as_ref() {
                    Some(ctx) => render_task.buffer_map.clear(ctx),
                    None => (),
                }
            }

            debug!("render_task: shutdown_chan send");
            shutdown_chan.send(());
        });
    }

    fn start(&mut self) {
        debug!("render_task: beginning rendering loop");

        loop {
            match self.port.recv() {
                RenderMsg(render_layers) => {
                    self.epoch.next();
                    self.render_layers = render_layers;

                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        continue;
                    }

                    initialize_layers(&mut self.compositor,
                                      self.id,
                                      self.epoch,
                                      self.render_layers.as_slice());
                }
                ReRenderMsg(requests) => {
                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        continue;
                    }

                    for ReRenderRequest { buffer_requests, scale, layer_id, epoch }
                          in requests.move_iter() {
                        if self.epoch == epoch {
                            self.render(buffer_requests, scale, layer_id);
                        } else {
                            debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
                        }
                    }
                }
                UnusedBufferMsg(unused_buffers) => {
                    for buffer in unused_buffers.move_iter().rev() {
                        self.buffer_map.insert(native_graphics_context!(self), buffer);
                    }
                }
                PaintPermissionGranted => {
                    self.paint_permission = true;

                    // Here we assume that the main layer—the layer responsible for the page size—
                    // is the first layer. This is a pretty fragile assumption. It will be fixed
                    // once we use the layers-based scrolling infrastructure for all scrolling.
                    if self.render_layers.len() > 1 {
                        self.epoch.next();
                        initialize_layers(&mut self.compositor,
                                          self.id,
                                          self.epoch,
                                          self.render_layers.as_slice());
                    }
                }
                PaintPermissionRevoked => {
                    self.paint_permission = false;
                }
                ExitMsg(response_ch) => {
                    debug!("render_task: exitmsg response send");
                    response_ch.map(|ch| ch.send(()));
                    break;
                }
            }
        }
    }

    /// Renders one layer and sends the tiles back to the layer.
    ///
    /// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in
    /// one transaction, to avoid the user seeing inconsistent states.
    fn render(&mut self, tiles: Vec<BufferRequest>, scale: f32, layer_id: LayerId) {
        time::profile(time::RenderingCategory, self.time_profiler_chan.clone(), || {
            // FIXME: Try not to create a new array here.
            let mut new_buffers = vec!();

            // Find the appropriate render layer.
            let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
                Some(render_layer) => render_layer,
                None => return,
            };

            self.compositor.set_render_state(RenderingRenderState);

            // Divide up the layer into tiles.
            for tile in tiles.iter() {
                // Optimize the display list for this tile.
                let page_rect_au = geometry::f32_rect_to_au_rect(tile.page_rect);
                let optimizer = DisplayListOptimizer::new(render_layer.display_list.clone(),
                                                          page_rect_au);
                let display_list = optimizer.optimize();

                let width = tile.screen_rect.size.width;
                let height = tile.screen_rect.size.height;

                let size = Size2D(width as i32, height as i32);
                let draw_target = match self.graphics_context {
                    CpuGraphicsContext => {
                        DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
                    }
                    GpuGraphicsContext => {
                        // FIXME(pcwalton): Cache the components of draw targets
                        // (texture color buffer, renderbuffers) instead of recreating them.
                        let draw_target =
                            DrawTarget::new_with_fbo(self.opts.render_backend,
                                                     native_graphics_context!(self),
                                                     size,
                                                     B8G8R8A8);
                        draw_target.make_current();
                        draw_target
                    }
                };

                {
                    // Build the render context.
                    let mut ctx = RenderContext {
                        draw_target: &draw_target,
                        font_ctx: &mut self.font_ctx,
                        opts: &self.opts,
                        page_rect: tile.page_rect,
                        screen_rect: tile.screen_rect,
                    };

                    // Apply the translation to render the tile we want.
                    let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
                    let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
                    let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
                                                  -(tile.page_rect.origin.y) as AzFloat);
                    let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat),
                                                  -(render_layer.position.origin.y as AzFloat));

                    ctx.draw_target.set_transform(&matrix);

                    // Clear the buffer.
                    ctx.clear();

                    // Draw the display list.
                    profile(time::RenderingDrawingCategory, self.time_profiler_chan.clone(), || {
                        display_list.draw_into_context(&mut ctx);
                        ctx.draw_target.flush();
                    });
                }

                // Extract the texture from the draw target and place it into its slot in the
                // buffer. If using CPU rendering, upload it first.
                //
                // FIXME(pcwalton): We should supply the texture and native surface *to* the
                // draw target in GPU rendering mode, so that it doesn't have to recreate it.
                let buffer = match self.graphics_context {
                    CpuGraphicsContext => {
                        let buffer = match self.buffer_map.find(tile.screen_rect.size) {
                            Some(buffer) => {
                                let mut buffer = buffer;
                                buffer.rect = tile.page_rect;
                                buffer.screen_pos = tile.screen_rect;
                                buffer.resolution = scale;
                                buffer.native_surface.mark_wont_leak();
                                buffer
                            }
                            None => {
                                // Create an empty native surface. We mark it as not leaking
                                // in case it dies in transit to the compositor task.
                                let mut native_surface: NativeSurface =
                                    layers::platform::surface::NativeSurfaceMethods::new(
                                        native_graphics_context!(self),
                                        Size2D(width as i32, height as i32),
                                        width as i32 * 4);
                                native_surface.mark_wont_leak();

                                box LayerBuffer {
                                    native_surface: native_surface,
                                    rect: tile.page_rect,
                                    screen_pos: tile.screen_rect,
                                    resolution: scale,
                                    stride: (width * 4) as uint
                                }
                            }
                        };

                        draw_target.snapshot().get_data_surface().with_data(|data| {
                            buffer.native_surface.upload(native_graphics_context!(self), data);
                            debug!("RENDERER uploading to native surface {:d}",
                                   buffer.native_surface.get_id() as int);
                        });

                        buffer
                    }
                    GpuGraphicsContext => {
                        draw_target.make_current();
                        let StolenGLResources {
                            surface: native_surface
                        } = draw_target.steal_gl_resources().unwrap();

                        // We mark the native surface as not leaking in case the surfaces
                        // die on their way to the compositor task.
                        let mut native_surface: NativeSurface =
                            NativeSurfaceAzureMethods::from_azure_surface(native_surface);
                        native_surface.mark_wont_leak();

                        box LayerBuffer {
                            native_surface: native_surface,
                            rect: tile.page_rect,
                            screen_pos: tile.screen_rect,
                            resolution: scale,
                            stride: (width * 4) as uint
                        }
                    }
                };

                new_buffers.push(buffer);
            }

            let layer_buffer_set = box LayerBufferSet {
                buffers: new_buffers,
            };

            debug!("render_task: returning surface");
            self.compositor.paint(self.id, render_layer.id, layer_buffer_set, self.epoch);
            self.compositor.set_render_state(IdleRenderState);
        })
    }
}

<commit_msg>Batch replies in RenderTask::render<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/. */

//! The task that handles all rendering/painting.

use buffer_map::BufferMap;
use display_list::optimizer::DisplayListOptimizer;
use display_list::DisplayList;
use font_context::FontContext;
use render_context::RenderContext;

use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
use layers;
use native;
use rustrt::task;
use rustrt::task::TaskOpts;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerId};
use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderState, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
use servo_msg::constellation_msg::{RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::geometry;
use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time;
use std::comm::{Receiver, Sender, channel};
use sync::Arc;
use font_cache_task::FontCacheTask;

/// Information about a layer that layout sends to the painting task.
pub struct RenderLayer {
    /// A per-pipeline ID describing this layer that should be stable across reflows.
    pub id: LayerId,
    /// The display list describing the contents of this layer.
    pub display_list: Arc<DisplayList>,
    /// The position of the layer in pixels.
    pub position: Rect<uint>,
    /// The color of the background in this layer. Used for unrendered content.
    pub background_color: Color,
    /// The scrolling policy of this layer.
    pub scroll_policy: ScrollPolicy,
}

pub struct ReRenderRequest {
    pub buffer_requests: Vec<BufferRequest>,
    pub scale: f32,
    pub layer_id: LayerId,
    pub epoch: Epoch,
}

pub enum Msg {
    RenderMsg(SmallVec1<RenderLayer>),
    ReRenderMsg(Vec<ReRenderRequest>),
    UnusedBufferMsg(Vec<Box<LayerBuffer>>),
    PaintPermissionGranted,
    PaintPermissionRevoked,
    ExitMsg(Option<Sender<()>>),
}

#[deriving(Clone)]
pub struct RenderChan(Sender<Msg>);

impl RenderChan {
    pub fn new() -> (Receiver<Msg>, RenderChan) {
        let (chan, port) = channel();
        (port, RenderChan(chan))
    }

    pub fn send(&self, msg: Msg) {
        let &RenderChan(ref chan) = self;
        assert!(chan.send_opt(msg).is_ok(), "RenderChan.send: render port closed")
    }

    pub fn send_opt(&self, msg: Msg) -> Result<(), Msg> {
        let &RenderChan(ref chan) = self;
        chan.send_opt(msg)
    }
}

/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
pub enum GraphicsContext {
    CpuGraphicsContext,
    GpuGraphicsContext,
}

pub struct RenderTask<C> {
    id: PipelineId,
    port: Receiver<Msg>,
    compositor: C,
    constellation_chan: ConstellationChan,
    font_ctx: Box<FontContext>,
    opts: Opts,

    /// A channel to the time profiler.
    time_profiler_chan: TimeProfilerChan,

    /// The graphics context to use.
    graphics_context: GraphicsContext,

    /// The native graphics context.
    native_graphics_context: Option<NativePaintingGraphicsContext>,

    /// The layers to be rendered.
    render_layers: SmallVec1<RenderLayer>,

    /// Permission to send paint messages to the compositor
    paint_permission: bool,

    /// A counter for epoch messages
    epoch: Epoch,

    /// A data structure to store unused LayerBuffers
    buffer_map: BufferMap<Box<LayerBuffer>>,
}

// If we implement this as a function, we get borrowck errors from borrowing
// the whole RenderTask struct.
macro_rules! native_graphics_context(
    ($task:expr) => (
        $task.native_graphics_context.as_ref().expect("Need a graphics context to do rendering")
    )
)

fn initialize_layers<C:RenderListener>(
                     compositor: &mut C,
                     pipeline_id: PipelineId,
                     epoch: Epoch,
                     render_layers: &[RenderLayer]) {
    let metadata = render_layers.iter().map(|render_layer| {
        LayerMetadata {
            id: render_layer.id,
            position: render_layer.position,
            background_color: render_layer.background_color,
            scroll_policy: render_layer.scroll_policy,
        }
    }).collect();
    compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
}

impl<C:RenderListener + Send> RenderTask<C> {
    pub fn create(id: PipelineId,
                  port: Receiver<Msg>,
                  compositor: C,
                  constellation_chan: ConstellationChan,
                  font_cache_task: FontCacheTask,
                  failure_msg: Failure,
                  opts: Opts,
                  time_profiler_chan: TimeProfilerChan,
                  shutdown_chan: Sender<()>) {

        let ConstellationChan(c) = constellation_chan.clone();
        let fc = font_cache_task.clone();

        let mut task_opts = TaskOpts::new();
        task_opts.name = Some("RenderTask".into_maybe_owned());
        task_opts.on_exit = Some(proc(result: task::Result) {
            match result {
                Ok(()) => {},
                Err(..) => {
                    c.send(FailureMsg(failure_msg));
                }
            }
        });
        native::task::spawn_opts(task_opts, proc() {
            { // Ensures RenderTask and graphics context are destroyed before shutdown msg
                let native_graphics_context = compositor.get_graphics_metadata().map(
                    |md| NativePaintingGraphicsContext::from_metadata(&md));
                let cpu_painting = opts.cpu_painting;

                // FIXME: rust/#5967
                let mut render_task = RenderTask {
                    id: id,
                    port: port,
                    compositor: compositor,
                    constellation_chan: constellation_chan,
                    font_ctx: box FontContext::new(fc.clone()),
                    opts: opts,
                    time_profiler_chan: time_profiler_chan,

                    graphics_context: if cpu_painting {
                        CpuGraphicsContext
                    } else {
                        GpuGraphicsContext
                    },

                    native_graphics_context: native_graphics_context,

                    render_layers: SmallVec1::new(),

                    paint_permission: false,
                    epoch: Epoch(0),
                    buffer_map: BufferMap::new(10000000),
                };

                render_task.start();

                // Destroy all the buffers.
                match render_task.native_graphics_context.as_ref() {
                    Some(ctx) => render_task.buffer_map.clear(ctx),
                    None => (),
                }
            }

            debug!("render_task: shutdown_chan send");
            shutdown_chan.send(());
        });
    }

    fn start(&mut self) {
        debug!("render_task: beginning rendering loop");

        loop {
            match self.port.recv() {
                RenderMsg(render_layers) => {
                    self.epoch.next();
                    self.render_layers = render_layers;

                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        continue;
                    }

                    initialize_layers(&mut self.compositor,
                                      self.id,
                                      self.epoch,
                                      self.render_layers.as_slice());
                }
                ReRenderMsg(requests) => {
                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        continue;
                    }

                    let mut replies = Vec::new();
                    for ReRenderRequest { buffer_requests, scale, layer_id, epoch }
                          in requests.move_iter() {
                        if self.epoch == epoch {
                            self.render(&mut replies, buffer_requests, scale, layer_id);
                        } else {
                            debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
                        }
                    }

                    debug!("render_task: returning surfaces");
                    for (layer_id, layer_buffer_set) in replies.move_iter() {
                        self.compositor.paint(self.id, layer_id, layer_buffer_set, self.epoch);
                    }
                }
                UnusedBufferMsg(unused_buffers) => {
                    for buffer in unused_buffers.move_iter().rev() {
                        self.buffer_map.insert(native_graphics_context!(self), buffer);
                    }
                }
                PaintPermissionGranted => {
                    self.paint_permission = true;

                    // Here we assume that the main layer—the layer responsible for the page size—
                    // is the first layer. This is a pretty fragile assumption. It will be fixed
                    // once we use the layers-based scrolling infrastructure for all scrolling.
                    if self.render_layers.len() > 1 {
                        self.epoch.next();
                        initialize_layers(&mut self.compositor,
                                          self.id,
                                          self.epoch,
                                          self.render_layers.as_slice());
                    }
                }
                PaintPermissionRevoked => {
                    self.paint_permission = false;
                }
                ExitMsg(response_ch) => {
                    debug!("render_task: exitmsg response send");
                    response_ch.map(|ch| ch.send(()));
                    break;
                }
            }
        }
    }

    /// Renders one layer and sends the tiles back to the layer.
    ///
    /// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in
    /// one transaction, to avoid the user seeing inconsistent states.
    fn render(&mut self,
              replies: &mut Vec<(LayerId, Box<LayerBufferSet>)>,
              tiles: Vec<BufferRequest>,
              scale: f32,
              layer_id: LayerId) {
        time::profile(time::RenderingCategory, self.time_profiler_chan.clone(), || {
            // FIXME: Try not to create a new array here.
            let mut new_buffers = vec!();

            // Find the appropriate render layer.
            let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
                Some(render_layer) => render_layer,
                None => return,
            };

            self.compositor.set_render_state(RenderingRenderState);

            // Divide up the layer into tiles.
            for tile in tiles.iter() {
                // Optimize the display list for this tile.
                let page_rect_au = geometry::f32_rect_to_au_rect(tile.page_rect);
                let optimizer = DisplayListOptimizer::new(render_layer.display_list.clone(),
                                                          page_rect_au);
                let display_list = optimizer.optimize();

                let width = tile.screen_rect.size.width;
                let height = tile.screen_rect.size.height;

                let size = Size2D(width as i32, height as i32);
                let draw_target = match self.graphics_context {
                    CpuGraphicsContext => {
                        DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
                    }
                    GpuGraphicsContext => {
                        // FIXME(pcwalton): Cache the components of draw targets
                        // (texture color buffer, renderbuffers) instead of recreating them.
                        let draw_target =
                            DrawTarget::new_with_fbo(self.opts.render_backend,
                                                     native_graphics_context!(self),
                                                     size,
                                                     B8G8R8A8);
                        draw_target.make_current();
                        draw_target
                    }
                };

                {
                    // Build the render context.
                    let mut ctx = RenderContext {
                        draw_target: &draw_target,
                        font_ctx: &mut self.font_ctx,
                        opts: &self.opts,
                        page_rect: tile.page_rect,
                        screen_rect: tile.screen_rect,
                    };

                    // Apply the translation to render the tile we want.
                    let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
                    let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
                    let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
                                                  -(tile.page_rect.origin.y) as AzFloat);
                    let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat),
                                                  -(render_layer.position.origin.y as AzFloat));

                    ctx.draw_target.set_transform(&matrix);

                    // Clear the buffer.
                    ctx.clear();

                    // Draw the display list.
                    profile(time::RenderingDrawingCategory, self.time_profiler_chan.clone(), || {
                        display_list.draw_into_context(&mut ctx);
                        ctx.draw_target.flush();
                    });
                }

                // Extract the texture from the draw target and place it into its slot in the
                // buffer. If using CPU rendering, upload it first.
                //
                // FIXME(pcwalton): We should supply the texture and native surface *to* the
                // draw target in GPU rendering mode, so that it doesn't have to recreate it.
                let buffer = match self.graphics_context {
                    CpuGraphicsContext => {
                        let buffer = match self.buffer_map.find(tile.screen_rect.size) {
                            Some(buffer) => {
                                let mut buffer = buffer;
                                buffer.rect = tile.page_rect;
                                buffer.screen_pos = tile.screen_rect;
                                buffer.resolution = scale;
                                buffer.native_surface.mark_wont_leak();
                                buffer
                            }
                            None => {
                                // Create an empty native surface. We mark it as not leaking
                                // in case it dies in transit to the compositor task.
                                let mut native_surface: NativeSurface =
                                    layers::platform::surface::NativeSurfaceMethods::new(
                                        native_graphics_context!(self),
                                        Size2D(width as i32, height as i32),
                                        width as i32 * 4);
                                native_surface.mark_wont_leak();

                                box LayerBuffer {
                                    native_surface: native_surface,
                                    rect: tile.page_rect,
                                    screen_pos: tile.screen_rect,
                                    resolution: scale,
                                    stride: (width * 4) as uint
                                }
                            }
                        };

                        draw_target.snapshot().get_data_surface().with_data(|data| {
                            buffer.native_surface.upload(native_graphics_context!(self), data);
                            debug!("RENDERER uploading to native surface {:d}",
                                   buffer.native_surface.get_id() as int);
                        });

                        buffer
                    }
                    GpuGraphicsContext => {
                        draw_target.make_current();
                        let StolenGLResources {
                            surface: native_surface
                        } = draw_target.steal_gl_resources().unwrap();

                        // We mark the native surface as not leaking in case the surfaces
                        // die on their way to the compositor task.
                        let mut native_surface: NativeSurface =
                            NativeSurfaceAzureMethods::from_azure_surface(native_surface);
                        native_surface.mark_wont_leak();

                        box LayerBuffer {
                            native_surface: native_surface,
                            rect: tile.page_rect,
                            screen_pos: tile.screen_rect,
                            resolution: scale,
                            stride: (width * 4) as uint
                        }
                    }
                };

                new_buffers.push(buffer);
            }

            let layer_buffer_set = box LayerBufferSet {
                buffers: new_buffers,
            };

            replies.push((render_layer.id, layer_buffer_set));
            self.compositor.set_render_state(IdleRenderState);
        })
    }
}

<|endoftext|>


<commit_before>//! Types for the *m.room.join_rules* event.

use ruma_events_macros::StateEventContent;
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};

use crate::StateEvent;

/// Describes how users are allowed to join the room.
pub type JoinRulesEvent = StateEvent<JoinRulesEventContent>;

/// The payload for `JoinRulesEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, StateEventContent)]
#[ruma_event(type = "m.room.join_rules")]
pub struct JoinRulesEventContent {
    /// The type of rules used for users wishing to join this room.
    #[ruma_event(skip_redaction)]
    pub join_rule: JoinRule,
}

/// The rule used for users wishing to join this room.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Deserialize, Serialize)]
#[non_exhaustive]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum JoinRule {
    /// A user who wishes to join the room must first receive an invite to the room from someone
    /// already inside of the room.
    Invite,

    /// Reserved but not yet implemented by the Matrix specification.
    Knock,

    /// Reserved but not yet implemented by the Matrix specification.
    Private,

    /// Anyone can join the room without any prior action.
    Public,
}
<commit_msg>Mark JoinRulesEventContent as non_exhaustive<commit_after>//! Types for the *m.room.join_rules* event.

use ruma_events_macros::StateEventContent;
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};

use crate::StateEvent;

/// Describes how users are allowed to join the room.
pub type JoinRulesEvent = StateEvent<JoinRulesEventContent>;

/// The payload for `JoinRulesEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, StateEventContent)]
#[non_exhaustive]
#[ruma_event(type = "m.room.join_rules")]
pub struct JoinRulesEventContent {
    /// The type of rules used for users wishing to join this room.
    #[ruma_event(skip_redaction)]
    pub join_rule: JoinRule,
}

impl JoinRulesEventContent {
    /// Creates a new `JoinRulesEventContent` with the given rule.
    pub fn new(join_rule: JoinRule) -> Self {
        Self { join_rule }
    }
}

/// The rule used for users wishing to join this room.
#[derive(Clone, Copy, Debug, PartialEq, Display, EnumString, Deserialize, Serialize)]
#[non_exhaustive]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum JoinRule {
    /// A user who wishes to join the room must first receive an invite to the room from someone
    /// already inside of the room.
    Invite,

    /// Reserved but not yet implemented by the Matrix specification.
    Knock,

    /// Reserved but not yet implemented by the Matrix specification.
    Private,

    /// Anyone can join the room without any prior action.
    Public,
}
<|endoftext|>


<commit_before>#![feature(lang_items, core_intrinsics, panic_implementation)]
#![no_std]
#![no_main]

// Pull in the system libc library for what crt0.o likely requires.
extern crate multiboot;

use core::mem;
use core::panic::PanicInfo;
use core::slice;
use multiboot::{Multiboot, PAddr};

#[cfg(feature = "nightly")]
use core::panic::PanicInfo;

pub fn paddr_to_slice<'a>(p: multiboot::PAddr, sz: usize) -> Option<&'a [u8]> {
    unsafe {
        let ptr = mem::transmute(p);
        Some(slice::from_raw_parts(ptr, sz))
    }
}

/// mboot_ptr is the initial pointer to the multiboot structure
/// provided in %ebx on start-up.
pub fn use_multiboot(mboot_ptr: PAddr) {
    unsafe {
        Multiboot::new(mboot_ptr, paddr_to_slice);
    }
}

#[cfg(feature = "nightly")]
#[no_mangle]
#[main]
pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
    use_multiboot(0x0);
    0
}

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

#[lang = "eh_unwind_resume"]
#[no_mangle]
pub extern "C" fn rust_eh_unwind_resume() {}

#[panic_implementation]
#[no_mangle]
pub fn panic_impl(_info: &PanicInfo) -> ! {
    loop {}
}
<commit_msg>Update test.<commit_after>extern crate core;
extern crate multiboot;

use core::mem;
use core::slice;
use multiboot::{Multiboot, PAddr};

pub fn paddr_to_slice<'a>(p: multiboot::PAddr, sz: usize) -> Option<&'a [u8]> {
    unsafe {
        let ptr = mem::transmute(p);
        Some(slice::from_raw_parts(ptr, sz))
    }
}

/// mboot_ptr is the initial pointer to the multiboot structure
/// provided in %ebx on start-up.
pub fn use_multiboot(mboot_ptr: PAddr) {
    unsafe {
        Multiboot::new(mboot_ptr, paddr_to_slice);
    }
}

fn main() {
    use_multiboot(0x0);
}
<|endoftext|>


<commit_before>// Copyright 2018 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(test, feature(test))]
#[cfg(test)]
extern crate test;

extern crate rand;
extern crate slog;
extern crate time;

extern crate tikv;

mod kv_generator;
mod logging;
mod macros;
mod security;

use std::env;

pub use kv_generator::*;
pub use logging::*;
pub use macros::*;
pub use security::*;

pub fn setup_for_ci() {
    if env::var("CI").is_ok() && env::var("LOG_FILE").is_ok() {
        logging::init_log_for_test();
    }
    if env::var("PANIC_ABORT").is_ok() {
        // Panics as aborts, it's helpful for debugging,
        // but also stops tests immediately.
        tikv::util::set_panic_hook(true, "./");
    }

    // HACK! Always use epollex in tests on Linux.
    // See more: https://github.com/grpc/grpc/blob/v1.17.2/src/core/lib/iomgr/ev_posix.cc#L124
    #[cfg(target_os = "linux")]
    env::set_var("GRPC_POLL_STRATEGY", "epollex");

    tikv::util::check_environment_variables();
}
<commit_msg>test: fix epollex error when some Linus versions don't contain that engine (#4114)<commit_after>// Copyright 2018 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(test, feature(test))]
#[cfg(test)]
extern crate test;

extern crate rand;
extern crate slog;
extern crate time;

extern crate tikv;

mod kv_generator;
mod logging;
mod macros;
mod security;

use std::env;

pub use kv_generator::*;
pub use logging::*;
pub use macros::*;
pub use security::*;

pub fn setup_for_ci() {
    if env::var("CI").is_ok() {
        if env::var("LOG_FILE").is_ok() {
            logging::init_log_for_test();
        }

        // HACK! Use `epollex` as the polling engine for gRPC when running CI tests on
        // Linux and it hasn't been set before.
        // See more: https://github.com/grpc/grpc/blob/v1.17.2/src/core/lib/iomgr/ev_posix.cc#L124
        // See more: https://grpc.io/grpc/core/md_doc_core_grpc-polling-engines.html
        #[cfg(target_os = "linux")]
        {
            if env::var("GRPC_POLL_STRATEGY").is_err() {
                env::set_var("GRPC_POLL_STRATEGY", "epollex");
            }
        }
    }

    if env::var("PANIC_ABORT").is_ok() {
        // Panics as aborts, it's helpful for debugging,
        // but also stops tests immediately.
        tikv::util::set_panic_hook(true, "./");
    }

    tikv::util::check_environment_variables();
}
<|endoftext|>


<commit_before>extern crate rsoundio;
extern crate rci;

use rci::Ci;

use std::thread;
use std::time::Duration;

#[test]
fn test_outstream() {
    if Ci::new().is_some() {
        ::std::process::exit(0)
    }
    let sio = rsoundio::SoundIo::new();
    sio.connect_backend(rsoundio::SioBackend::Dummy).unwrap();
    let current_backend = sio.current_backend().unwrap();
    assert_eq!(current_backend, rsoundio::SioBackend::Dummy);
    sio.flush_events();
    let dev_idx = sio.default_output_device_index().unwrap();
    let dev = sio.output_device(dev_idx).unwrap();
    let mut stream = dev.create_outstream().unwrap();
    stream.open().unwrap();
    assert!(stream.sample_rate() > 0);
    let layout = stream.layout();
    assert_eq!(layout.channel_count(), 2);
    let cb = |out: rsoundio::OutStream, min_frame_count: u32, max_frame_count: u32| {
        let l: Vec<f32> = (0..max_frame_count as usize)
                              .map(|i| {
                                  (i as f32 * ((2.0 * ::std::f32::consts::PI * 440.0) / 48_000f32))
                                      .sin()
                              })
                              .collect();
        let r = l.clone();
        let frames = vec![l, r];
        let frames_written = out.write_stream_f32(min_frame_count, &frames).unwrap();
        assert!(frames_written > 0);
    };
    stream.register_write_callback(cb);
    let ucb = |_: rsoundio::OutStream| println!("Underflow!");
    stream.register_underflow_callback(ucb);
    let ecb = |_: rsoundio::OutStream, err: rsoundio::SioError| println!("Error: {}", err);
    stream.register_error_callback(ecb);
    stream.start().unwrap();
    thread::sleep(Duration::new(1, 0));
    assert!(stream.pause().is_none());
    thread::sleep(Duration::new(1, 0));
    assert!(stream.unpause().is_none());
    assert!(stream.latency().is_ok());
    assert!(stream.clear_buffer().is_none());
    thread::sleep(Duration::new(1, 0));
}
<commit_msg>Move values into closure to avoid lifetime errors<commit_after>extern crate rsoundio;
extern crate rci;

use rci::Ci;

use std::thread;
use std::time::Duration;

#[test]
fn test_outstream() {
    if Ci::new().is_some() {
        ::std::process::exit(0)
    }
    let sio = rsoundio::SoundIo::new();
    sio.connect_backend(rsoundio::SioBackend::Dummy).unwrap();
    let current_backend = sio.current_backend().unwrap();
    assert_eq!(current_backend, rsoundio::SioBackend::Dummy);
    sio.flush_events();
    let dev_idx = sio.default_output_device_index().unwrap();
    let dev = sio.output_device(dev_idx).unwrap();
    let mut stream = dev.create_outstream().unwrap();
    stream.open().unwrap();
    assert!(stream.sample_rate() > 0);
    let layout = stream.layout();
    assert_eq!(layout.channel_count(), 2);
    let cb = move |out: rsoundio::OutStream, min_frame_count: u32, max_frame_count: u32| {
        let l: Vec<f32> = (0..max_frame_count as usize)
                              .map(|i| {
                                  (i as f32 * ((2.0 * ::std::f32::consts::PI * 440.0) / 48_000f32))
                                      .sin()
                              })
                              .collect();
        let r = l.clone();
        let frames = vec![l, r];
        let frames_written = out.write_stream_f32(min_frame_count, &frames).unwrap();
        assert!(frames_written > 0);
    };
    stream.register_write_callback(cb);
    let ucb = |_: rsoundio::OutStream| println!("Underflow!");
    stream.register_underflow_callback(ucb);
    let ecb = |_: rsoundio::OutStream, err: rsoundio::SioError| println!("Error: {}", err);
    stream.register_error_callback(ecb);
    stream.start().unwrap();
    thread::sleep(Duration::new(1, 0));
    assert!(stream.pause().is_none());
    thread::sleep(Duration::new(1, 0));
    assert!(stream.unpause().is_none());
    assert!(stream.latency().is_ok());
    assert!(stream.clear_buffer().is_none());
    thread::sleep(Duration::new(1, 0));
}
<|endoftext|>


<commit_before>use super::dump::dump_tree;
use super::node::read_unchecked;
use super::node::write_node;
use super::node::NodeWriteGuard;
use super::prune::*;
use super::search::mut_search;
use super::search::MutSearchResult;
use super::NodeCellRef;
use super::*;
use super::{external, BPlusTree};
use super::{LevelTree, MIN_ENTRY_KEY};
use itertools::Itertools;
use std::fmt::Debug;
use std::sync::atomic::Ordering::Relaxed;

pub const LEVEL_PAGE_DIFF_MULTIPLIER: usize = 4;
pub const LEVEL_TREE_DEPTH: u32 = 3;

pub const LEVEL_M: usize = 8;
pub const LEVEL_1: usize = LEVEL_M * LEVEL_PAGE_DIFF_MULTIPLIER;
pub const LEVEL_2: usize = LEVEL_1 * LEVEL_PAGE_DIFF_MULTIPLIER;

pub const NUM_LEVELS: usize = 3;

// Select left most leaf nodes and acquire their write guard
fn select<KS, PS>(node: &NodeCellRef) -> Vec<NodeWriteGuard<KS, PS>>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let search = mut_search::<KS, PS>(node, &*MIN_ENTRY_KEY);
    match search {
        MutSearchResult::External => {
            trace!("Acquiring first node");
            let first_node = write_node(node);
            assert!(read_unchecked::<KS, PS>(first_node.left_ref().unwrap()).is_none());
            let mut collected_keys = first_node.len();
            let mut collected = vec![first_node];
            let target_keys = KS::slice_len();
            let target_guards = LEVEL_2;
            while collected_keys < target_keys && collected.len() < target_guards {
                trace!("Acquiring select collection node");
                let right = write_node(collected.last().unwrap().right_ref().unwrap());
                if right.is_none() {
                    // Early break for reach the end of the linked list
                    // Should not be possible, will warn
                    warn!("Searching node to move and reach the end, maybe this is not the right parameter");
                    break;
                } else {
                    debug_assert!(!right.is_empty(), "found empty node on selection!!!");
                    debug_assert!(!read_unchecked::<KS, PS>(right.right_ref().unwrap()).is_none());
                    debug_assert!(
                        !read_unchecked::<KS, PS>(right.right_ref().unwrap()).is_empty_node()
                    );
                    collected_keys += right.len();
                    collected.push(right);
                }
            }
            return collected;
        }
        MutSearchResult::Internal(node) => select::<KS, PS>(&node),
    }
}

pub fn level_merge<KS, PS>(src_tree: &BPlusTree<KS, PS>, dest_tree: &dyn LevelTree) -> usize
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut left_most_leaf_guards = select::<KS, PS>(&src_tree.get_root());
    let merge_page_len = left_most_leaf_guards.len();
    let num_keys_moved;
    let left_most_id = left_most_leaf_guards.first().unwrap().ext_id();
    let prune_bound = left_most_leaf_guards.last().unwrap().right_bound().clone();

    trace!("Merge selected {} pages", left_most_leaf_guards.len());
    if cfg!(debug_assertions) {
        if left_most_id != src_tree.head_page_id {
            dump_tree(src_tree, "level_lsm_merge_failure_dump.json");
        }
    }
    debug_assert_eq!(
        left_most_id,
        src_tree.head_page_id,
        "{}",
        left_most_leaf_guards.first().unwrap().type_name()
    );

    // merge to dest_tree
    {
        let deleted_keys = &src_tree.deleted;
        let mut merged_deleted_keys = vec![];
        let keys: Vec<EntryKey> = left_most_leaf_guards
            .iter()
            .map(|g| &g.keys()[..g.len()])
            .flatten()
            .filter(|&k| {
                if deleted_keys.contains(k) {
                    merged_deleted_keys.push(k.clone());
                    false
                } else {
                    true
                }
            })
            .cloned()
            .collect_vec();
        num_keys_moved = keys.len();
        trace!("Merge selected keys {:?}", keys.len());
        dest_tree.merge_with_keys(box keys);
        for rk in &merged_deleted_keys {
            deleted_keys.remove(rk);
        }
    }

    // adjust leaf left, right references
    let mut removed_nodes = AlteredNodes {
        removed: vec![],
        added: vec![],
        key_modified: vec![],
    };
    {
        let right_right_most = left_most_leaf_guards
            .last()
            .unwrap()
            .right_ref()
            .unwrap()
            .clone();

        let left_left_most = left_most_leaf_guards
            .first()
            .unwrap()
            .left_ref()
            .unwrap()
            .clone();

        debug_assert!(read_unchecked::<KS, PS>(&left_left_most).is_none());
        debug_assert!(!read_unchecked::<KS, PS>(&right_right_most).is_none());
        debug_assert!(read_unchecked::<KS, PS>(&right_right_most).is_ext());
        debug_assert!(!right_right_most.ptr_eq(left_most_leaf_guards.last().unwrap().node_ref()));

        for g in &mut left_most_leaf_guards {
            external::make_deleted(&g.ext_id());
            removed_nodes
                .removed
                .push((g.right_bound().clone(), g.node_ref().clone()));
            g.make_empty_node(false);
            g.right_ref_mut().map(|rr| *rr = right_right_most.clone());
            g.left_ref_mut().map(|lr| *lr = left_left_most.clone());
        }

        trace!("Acquiring new first node");
        let mut new_first_node = write_node::<KS, PS>(&right_right_most);
        let mut new_first_node_ext = new_first_node.extnode_mut(src_tree);
        trace!(
            "Right most original id is {:?}, now is {:?}",
            new_first_node_ext.id, src_tree.head_page_id
        );
        trace!(
            "New first node right is {:?}",
            read_unchecked::<KS, PS>(&new_first_node_ext.next).ext_id()
        );

        new_first_node_ext.id = src_tree.head_page_id;
        new_first_node_ext.prev = left_left_most;

        debug_assert!(&new_first_node_ext.right_bound > &prune_bound);
    }

    // cleanup upper level references
    prune::<KS, PS>(&src_tree.get_root(), box removed_nodes, 0);

    src_tree.len.fetch_sub(num_keys_moved, Relaxed);

    trace!("Merge completed");

    merge_page_len
}
<commit_msg>reveal problems<commit_after>use super::dump::dump_tree;
use super::node::read_unchecked;
use super::node::write_node;
use super::node::NodeWriteGuard;
use super::prune::*;
use super::search::mut_search;
use super::search::MutSearchResult;
use super::NodeCellRef;
use super::*;
use super::{external, BPlusTree};
use super::{LevelTree, MIN_ENTRY_KEY};
use itertools::Itertools;
use std::fmt::Debug;
use std::sync::atomic::Ordering::Relaxed;

pub const LEVEL_PAGE_DIFF_MULTIPLIER: usize = 4;
pub const LEVEL_TREE_DEPTH: u32 = 3;

pub const LEVEL_M: usize = 8;
pub const LEVEL_1: usize = LEVEL_M * LEVEL_PAGE_DIFF_MULTIPLIER;
pub const LEVEL_2: usize = LEVEL_1 * LEVEL_PAGE_DIFF_MULTIPLIER;

pub const NUM_LEVELS: usize = 3;

// Select left most leaf nodes and acquire their write guard
fn select<KS, PS>(node: &NodeCellRef) -> Vec<NodeWriteGuard<KS, PS>>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let search = mut_search::<KS, PS>(node, &*MIN_ENTRY_KEY);
    match search {
        MutSearchResult::External => {
            trace!("Acquiring first node");
            let first_node = write_node(node);
            assert!(read_unchecked::<KS, PS>(first_node.left_ref().unwrap()).is_none());
            debug!("This level has {:?} external nodes to select", num_pages(&first_node));
            let mut collected = vec![first_node];
            let target_guards = 16; // pages to collect
            while collected.len() < target_guards {
                trace!("Acquiring select collection node");
                let right = write_node(collected.last().unwrap().right_ref().unwrap());
                if right.is_none() {
                    // Early break for reach the end of the linked list
                    // Should not be possible, will warn
                    warn!("Searching node to move and reach the end, maybe this is not the right parameter");
                    break;
                } else {
                    debug_assert!(!right.is_empty(), "found empty node on selection!!!");
                    debug_assert!(!read_unchecked::<KS, PS>(right.right_ref().unwrap()).is_none());
                    debug_assert!(
                        !read_unchecked::<KS, PS>(right.right_ref().unwrap()).is_empty_node()
                    );
                    collected.push(right);
                }
            }
            return collected;
        }
        MutSearchResult::Internal(node) => select::<KS, PS>(&node),
    }
}

fn num_pages<KS, PS>(head_page: &NodeWriteGuard<KS, PS>) -> (usize, usize)
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut num = 0;
    let mut non_empty = 0;
    let mut node_ref = head_page.node_ref().clone();
    loop {
        let node = read_unchecked::<KS, PS>(&node_ref);
        if node.is_none() {
            break;
        }
        if !node.is_empty() {
            non_empty += 1;
        }
        if let Some(node) = node.right_ref() {
            node_ref = node.clone()
        } else {
            break;
        }
        num += 1;
    }
    (num, non_empty)
}

pub fn level_merge<KS, PS>(src_tree: &BPlusTree<KS, PS>, dest_tree: &dyn LevelTree) -> usize
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut left_most_leaf_guards = select::<KS, PS>(&src_tree.get_root());
    let merge_page_len = left_most_leaf_guards.len();
    let num_keys_moved;
    let left_most_id = left_most_leaf_guards.first().unwrap().ext_id();
    let prune_bound = left_most_leaf_guards.last().unwrap().right_bound().clone();

    debug!("Merge selected {} pages", left_most_leaf_guards.len());
    if cfg!(debug_assertions) {
        if left_most_id != src_tree.head_page_id {
            dump_tree(src_tree, "level_lsm_merge_failure_dump.json");
        }
    }
    debug_assert_eq!(
        left_most_id,
        src_tree.head_page_id,
        "{}",
        left_most_leaf_guards.first().unwrap().type_name()
    );

    // merge to dest_tree
    {
        let deleted_keys = &src_tree.deleted;
        let mut merged_deleted_keys = vec![];
        let keys: Vec<EntryKey> = left_most_leaf_guards
            .iter()
            .map(|g| &g.keys()[..g.len()])
            .flatten()
            .filter(|&k| {
                if deleted_keys.contains(k) {
                    merged_deleted_keys.push(k.clone());
                    false
                } else {
                    true
                }
            })
            .cloned()
            .collect_vec();
        num_keys_moved = keys.len();
        debug!("Merging {} keys, have {}", num_keys_moved, src_tree.len.load(Relaxed));
        dest_tree.merge_with_keys(box keys);
        for rk in &merged_deleted_keys {
            deleted_keys.remove(rk);
        }
    }

    // adjust leaf left, right references
    let mut removed_nodes = AlteredNodes {
        removed: vec![],
        added: vec![],
        key_modified: vec![],
    };
    {
        let right_right_most = left_most_leaf_guards
            .last()
            .unwrap()
            .right_ref()
            .unwrap()
            .clone();

        let left_left_most = left_most_leaf_guards
            .first()
            .unwrap()
            .left_ref()
            .unwrap()
            .clone();

        debug_assert!(read_unchecked::<KS, PS>(&left_left_most).is_none());
        debug_assert!(!read_unchecked::<KS, PS>(&right_right_most).is_none());
        debug_assert!(read_unchecked::<KS, PS>(&right_right_most).is_ext());
        debug_assert!(!right_right_most.ptr_eq(left_most_leaf_guards.last().unwrap().node_ref()));

        for g in &mut left_most_leaf_guards {
            external::make_deleted(&g.ext_id());
            removed_nodes
                .removed
                .push((g.right_bound().clone(), g.node_ref().clone()));
            g.make_empty_node(false);
            g.right_ref_mut().map(|rr| *rr = right_right_most.clone());
            g.left_ref_mut().map(|lr| *lr = left_left_most.clone());
        }

        trace!("Acquiring new first node");
        let mut new_first_node = write_node::<KS, PS>(&right_right_most);
        let mut new_first_node_ext = new_first_node.extnode_mut(src_tree);
        trace!(
            "Right most original id is {:?}, now is {:?}",
            new_first_node_ext.id, src_tree.head_page_id
        );
        trace!(
            "New first node right is {:?}",
            read_unchecked::<KS, PS>(&new_first_node_ext.next).ext_id()
        );

        new_first_node_ext.id = src_tree.head_page_id;
        new_first_node_ext.prev = left_left_most;

        debug_assert!(&new_first_node_ext.right_bound > &prune_bound);
    }

    // cleanup upper level references
    prune::<KS, PS>(&src_tree.get_root(), box removed_nodes, 0);

    src_tree.len.fetch_sub(num_keys_moved, Relaxed);

    trace!("Merge completed");

    merge_page_len
}
<|endoftext|>


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

use base::{
    AbstractCFTypeRef,
    CFAllocatorRef,
    CFIndex,
    CFTypeID,
    CFTypeRef,
    CFWrapper,
    kCFAllocatorDefault,
};
use std::cast;
use std::libc::c_void;
use std::ptr;
use std::vec;
use std::iterator::Enumerate;

pub type CFArrayRetainCallBack = *u8;
pub type CFArrayReleaseCallBack = *u8;
pub type CFArrayCopyDescriptionCallBack = *u8;
pub type CFArrayEqualCallBack = *u8;

pub struct CFArrayCallBacks {
    version: CFIndex,
    retain: CFArrayRetainCallBack,
    release: CFArrayReleaseCallBack,
    copyDescription: CFArrayCopyDescriptionCallBack,
    equal: CFArrayEqualCallBack,
}

struct __CFArray { private: () }
pub type CFArrayRef = *__CFArray;

impl AbstractCFTypeRef for CFArrayRef {
    fn as_type_ref(&self) -> CFTypeRef { *self as CFTypeRef }
    fn type_id() -> CFTypeID { unsafe { CFArrayGetTypeID() } }
}

// FIXME: Should be a newtype struct, but that fails due to a Rust compiler bug.
pub struct CFArray<ElemRefType> {
    contents: CFWrapper<CFArrayRef, ElemRefType, ()>
}

pub struct CFArrayIterator<'self, ElemRefType> {
    priv array: &'self CFArray<ElemRefType>,
    priv index: uint,
}

impl<'self, ElemRefType: AbstractCFTypeRef> Iterator<ElemRefType> for CFArrayIterator<'self, ElemRefType> {
    fn next(&mut self) -> Option<ElemRefType> {
        if self.index >= self.array.len() {
            None
        } else {
            let v = self.array[self.index];
            self.index += 1;
            Some(v)
        }
    }
}

impl<ElemRefType:AbstractCFTypeRef> CFArray<ElemRefType> {
    pub fn wrap_shared(array: CFArrayRef) -> CFArray<ElemRefType> {
        CFArray {
            contents: CFWrapper::wrap_shared(array)
        }
    }

    pub fn wrap_owned(array: CFArrayRef) -> CFArray<ElemRefType> {
        CFArray {
            contents: CFWrapper::wrap_owned(array)
        }
    }

    pub fn new(elems: &[ElemRefType]) -> CFArray<ElemRefType> {
        let array_ref: CFArrayRef;
        let elems_refs = do elems.map |e: &ElemRefType| { e.as_type_ref() };

        unsafe {
            array_ref = CFArrayCreate(kCFAllocatorDefault,
                                      cast::transmute::<*CFTypeRef, **c_void>(vec::raw::to_ptr(elems_refs)),
                                      elems.len() as CFIndex,
                                      ptr::to_unsafe_ptr(&kCFTypeArrayCallBacks));
        }

        CFArray {
            contents: CFWrapper::wrap_owned(array_ref)
        }
    }

    // Careful; the loop body must wrap the reference properly.
    // Generally, when array elements are Core Foundation objects (not
    // always true), they need to be wrapped with CFWrapper::wrap_shared.
    pub fn each<'t>(&'t self) -> CFArrayIterator<'t, ElemRefType> {
        CFArrayIterator {
            array: self,
            index: 0
        }
    }

    // Careful; the loop body must wrap the reference properly.
    // Generally, when array elements are Core Foundation objects (not
    // always true), they need to be wrapped with CFWrapper::wrap_shared.
    pub fn eachi<'t>(&'t self) -> Enumerate<CFArrayIterator<'t, ElemRefType>> {
        self.each().enumerate()
    }

    pub fn len(&self) -> uint {
        unsafe {
            return CFArrayGetCount(*self.contents.borrow_ref()) as uint;
        }
    }
}

impl<ElemRefType:AbstractCFTypeRef> Index<uint,ElemRefType> for CFArray<ElemRefType> {
    // Careful; the caller must wrap any returned reference properly.
    // Generally, when array elements are Core Foundation objects (not
    // always true), they need to be wrapped with CFWrapper::wrap_shared.
    fn index(&self, idx: &uint) -> ElemRefType {
        assert!(*idx < self.len());
        unsafe {
            let elem = CFArrayGetValueAtIndex(*self.contents.borrow_ref(), *idx as CFIndex);
            // Don't return a wrapped thing, since we don't know whether
            // it needs base::wrap_shared() or base::wrap_owned()
            cast::transmute::<*c_void,ElemRefType>(elem)
        }
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFArray.h
     */
    static kCFTypeArrayCallBacks: CFArrayCallBacks;

    fn CFArrayCreate(allocator: CFAllocatorRef, values: **c_void,
                     numValues: CFIndex, callBacks: *CFArrayCallBacks) -> CFArrayRef;
    // CFArrayCreateCopy
    // CFArrayBSearchValues
    // CFArrayContainsValue
    fn CFArrayGetCount(theArray: CFArrayRef) -> CFIndex;
    // CFArrayGetCountOfValue
    // CFArrayGetFirstIndexOfValue
    // CFArrayGetLastIndexOfValue
    // CFArrayGetValues
    fn CFArrayGetValueAtIndex(theArray: CFArrayRef, idx: CFIndex) -> *c_void;
    // CFArrayApplyFunction
    fn CFArrayGetTypeID() -> CFTypeID;
}

#[test]
fn should_box_and_unbox() {
    use number::CFNumber;

    let one = CFNumber::new(1 as i32);
    let two = CFNumber::new(2 as i32);
    let thr = CFNumber::new(3 as i32);
    let fou = CFNumber::new(4 as i32);
    let fiv = CFNumber::new(5 as i32);

    let arr = CFArray::new([
        *one.contents.borrow_ref(),
        *two.contents.borrow_ref(),
        *thr.contents.borrow_ref(),
        *fou.contents.borrow_ref(),
        *fiv.contents.borrow_ref()
    ]);

    let mut sum = 0i32;

    for elem in arr.iter() {
        sum += CFNumber::wrap_shared(elem).to_i32();
    }

    assert!(sum == 15);

    for elem in arr.iter() {
        sum += CFNumber::wrap_shared(elem).to_i32();
    }

    assert!(sum == 30);
}
<commit_msg>Rename iterators.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use base::{
    AbstractCFTypeRef,
    CFAllocatorRef,
    CFIndex,
    CFTypeID,
    CFTypeRef,
    CFWrapper,
    kCFAllocatorDefault,
};
use std::cast;
use std::libc::c_void;
use std::ptr;
use std::vec;

pub type CFArrayRetainCallBack = *u8;
pub type CFArrayReleaseCallBack = *u8;
pub type CFArrayCopyDescriptionCallBack = *u8;
pub type CFArrayEqualCallBack = *u8;

pub struct CFArrayCallBacks {
    version: CFIndex,
    retain: CFArrayRetainCallBack,
    release: CFArrayReleaseCallBack,
    copyDescription: CFArrayCopyDescriptionCallBack,
    equal: CFArrayEqualCallBack,
}

struct __CFArray { private: () }
pub type CFArrayRef = *__CFArray;

impl AbstractCFTypeRef for CFArrayRef {
    fn as_type_ref(&self) -> CFTypeRef { *self as CFTypeRef }
    fn type_id() -> CFTypeID { unsafe { CFArrayGetTypeID() } }
}

// FIXME: Should be a newtype struct, but that fails due to a Rust compiler bug.
pub struct CFArray<ElemRefType> {
    contents: CFWrapper<CFArrayRef, ElemRefType, ()>
}

pub struct CFArrayIterator<'self, ElemRefType> {
    priv array: &'self CFArray<ElemRefType>,
    priv index: uint,
}

impl<'self, ElemRefType: AbstractCFTypeRef> Iterator<ElemRefType> for CFArrayIterator<'self, ElemRefType> {
    fn next(&mut self) -> Option<ElemRefType> {
        if self.index >= self.array.len() {
            None
        } else {
            let v = self.array[self.index];
            self.index += 1;
            Some(v)
        }
    }
}

impl<ElemRefType:AbstractCFTypeRef> CFArray<ElemRefType> {
    pub fn wrap_shared(array: CFArrayRef) -> CFArray<ElemRefType> {
        CFArray {
            contents: CFWrapper::wrap_shared(array)
        }
    }

    pub fn wrap_owned(array: CFArrayRef) -> CFArray<ElemRefType> {
        CFArray {
            contents: CFWrapper::wrap_owned(array)
        }
    }

    pub fn new(elems: &[ElemRefType]) -> CFArray<ElemRefType> {
        let array_ref: CFArrayRef;
        let elems_refs = do elems.map |e: &ElemRefType| { e.as_type_ref() };

        unsafe {
            array_ref = CFArrayCreate(kCFAllocatorDefault,
                                      cast::transmute::<*CFTypeRef, **c_void>(vec::raw::to_ptr(elems_refs)),
                                      elems.len() as CFIndex,
                                      ptr::to_unsafe_ptr(&kCFTypeArrayCallBacks));
        }

        CFArray {
            contents: CFWrapper::wrap_owned(array_ref)
        }
    }

    // Careful; the loop body must wrap the reference properly.
    // Generally, when array elements are Core Foundation objects (not
    // always true), they need to be wrapped with CFWrapper::wrap_shared.
    pub fn iter<'t>(&'t self) -> CFArrayIterator<'t, ElemRefType> {
        CFArrayIterator {
            array: self,
            index: 0
        }
    }

    pub fn len(&self) -> uint {
        unsafe {
            return CFArrayGetCount(*self.contents.borrow_ref()) as uint;
        }
    }
}

impl<ElemRefType:AbstractCFTypeRef> Index<uint,ElemRefType> for CFArray<ElemRefType> {
    // Careful; the caller must wrap any returned reference properly.
    // Generally, when array elements are Core Foundation objects (not
    // always true), they need to be wrapped with CFWrapper::wrap_shared.
    fn index(&self, idx: &uint) -> ElemRefType {
        assert!(*idx < self.len());
        unsafe {
            let elem = CFArrayGetValueAtIndex(*self.contents.borrow_ref(), *idx as CFIndex);
            // Don't return a wrapped thing, since we don't know whether
            // it needs base::wrap_shared() or base::wrap_owned()
            cast::transmute::<*c_void,ElemRefType>(elem)
        }
    }
}

#[link_args="-framework CoreFoundation"]
#[nolink]
extern {
    /*
     * CFArray.h
     */
    static kCFTypeArrayCallBacks: CFArrayCallBacks;

    fn CFArrayCreate(allocator: CFAllocatorRef, values: **c_void,
                     numValues: CFIndex, callBacks: *CFArrayCallBacks) -> CFArrayRef;
    // CFArrayCreateCopy
    // CFArrayBSearchValues
    // CFArrayContainsValue
    fn CFArrayGetCount(theArray: CFArrayRef) -> CFIndex;
    // CFArrayGetCountOfValue
    // CFArrayGetFirstIndexOfValue
    // CFArrayGetLastIndexOfValue
    // CFArrayGetValues
    fn CFArrayGetValueAtIndex(theArray: CFArrayRef, idx: CFIndex) -> *c_void;
    // CFArrayApplyFunction
    fn CFArrayGetTypeID() -> CFTypeID;
}

#[test]
fn should_box_and_unbox() {
    use number::CFNumber;

    let one = CFNumber::new(1 as i32);
    let two = CFNumber::new(2 as i32);
    let thr = CFNumber::new(3 as i32);
    let fou = CFNumber::new(4 as i32);
    let fiv = CFNumber::new(5 as i32);

    let arr = CFArray::new([
        *one.contents.borrow_ref(),
        *two.contents.borrow_ref(),
        *thr.contents.borrow_ref(),
        *fou.contents.borrow_ref(),
        *fiv.contents.borrow_ref()
    ]);

    let mut sum = 0i32;

    for elem in arr.iter() {
        sum += CFNumber::wrap_shared(elem).to_i32();
    }

    assert!(sum == 15);

    for elem in arr.iter() {
        sum += CFNumber::wrap_shared(elem).to_i32();
    }

    assert!(sum == 30);
}
<|endoftext|>


<commit_before>//! Module containing functions for interacting with gluon's primitive types.
use std::string::String as StdString;
use std::result::Result as StdResult;

use {Variants, Error};
use primitives as prim;
use api::{generic, Generic, Getable, AsyncPushable, Array, RuntimeResult, primitive, WithVM};
use api::generic::A;
use gc::{Gc, Traverseable, DataDef, WriteOnly};
use Result;
use vm::{Thread, Status};
use value::{Def, GcStr, Repr, Value, ValueArray};
use stack::StackFrame;
use thread::ThreadInternal;
use types::VmInt;

fn array_length(array: Array<generic::A>) -> VmInt {
    array.len() as VmInt
}

fn array_index<'vm>(
    array: Array<'vm, Generic<generic::A>>,
    index: VmInt,
) -> RuntimeResult<Generic<generic::A>, String> {
    match array.get(index) {
        Some(value) => RuntimeResult::Return(value),
        None => RuntimeResult::Panic(format!("Index {} is out of range", index)),
    }
}

fn array_append<'vm>(
    lhs: Array<'vm, Generic<generic::A>>,
    rhs: Array<'vm, Generic<generic::A>>,
) -> RuntimeResult<Array<'vm, Generic<generic::A>>, Error> {
    struct Append<'b> {
        lhs: &'b ValueArray,
        rhs: &'b ValueArray,
    }

    impl<'b> Append<'b> {
        fn repr(&self) -> Repr {
            // Empty arrays don't have the correct representation set so choose the representation
            // of `rhs` if it is empty. (And if both are empty the representation does not matter).
            if self.lhs.len() == 0 {
                self.rhs.repr()
            } else {
                self.lhs.repr()
            }
        }
    }

    impl<'b> Traverseable for Append<'b> {
        fn traverse(&self, gc: &mut Gc) {
            self.lhs.traverse(gc);
            self.rhs.traverse(gc);
        }
    }

    unsafe impl<'b> DataDef for Append<'b> {
        type Value = ValueArray;
        fn size(&self) -> usize {
            let len = self.lhs.len() + self.rhs.len();
            ValueArray::size_of(self.repr(), len)
        }
        fn initialize<'w>(self, mut result: WriteOnly<'w, ValueArray>) -> &'w mut ValueArray {
            unsafe {
                let result = &mut *result.as_mut_ptr();
                result.set_repr(self.repr());
                result.initialize(self.lhs.iter().chain(self.rhs.iter()));
                result
            }
        }
    }
    let vm = lhs.vm();
    let value = {
        let mut context = vm.context();
        let result = context.alloc(Append {
            lhs: lhs.get_value_array(),
            rhs: rhs.get_value_array(),
        });
        match result {
            Ok(x) => x,
            Err(err) => return RuntimeResult::Panic(err),
        }
    };
    unsafe {
        RuntimeResult::Return(
            Getable::from_value(lhs.vm(), Variants::new(&Value::Array(value))).expect("Array"),
        )
    }
}

fn string_append(lhs: WithVM<&str>, rhs: &str) -> RuntimeResult<String, Error> {
    struct StrAppend<'b> {
        lhs: &'b str,
        rhs: &'b str,
    }

    impl<'b> Traverseable for StrAppend<'b> {}

    unsafe impl<'b> DataDef for StrAppend<'b> {
        type Value = ValueArray;
        fn size(&self) -> usize {
            use std::mem::size_of;
            size_of::<ValueArray>() + (self.lhs.len() + self.rhs.len()) * size_of::<u8>()
        }
        fn initialize<'w>(self, mut result: WriteOnly<'w, ValueArray>) -> &'w mut ValueArray {
            use value::Repr;
            unsafe {
                let result = &mut *result.as_mut_ptr();
                result.set_repr(Repr::Byte);
                result.unsafe_array_mut::<u8>().initialize(
                    self.lhs
                        .as_bytes()
                        .iter()
                        .chain(self.rhs.as_bytes())
                        .cloned(),
                );
                result
            }
        }
    }

    let vm = lhs.vm;
    let lhs = lhs.value;
    unsafe {
        let mut context = vm.context();
        let result = context.alloc(StrAppend { lhs: lhs, rhs: rhs });
        let value = match result {
            Ok(x) => GcStr::from_utf8_unchecked(x),
            Err(err) => return RuntimeResult::Panic(err),
        };
        RuntimeResult::Return(
            Getable::from_value(vm, Variants::new(&Value::String(value))).expect("Array"),
        )
    }
}

fn string_slice(s: &str, start: usize, end: usize) -> RuntimeResult<&str, String> {
    if s.is_char_boundary(start) && s.is_char_boundary(end) {
        RuntimeResult::Return(&s[start..end])
    } else {
        // Limit the amount of characters to print in the error message
        let mut iter = s.chars();
        for _ in iter.by_ref().take(256) {}
        RuntimeResult::Panic(format!(
            "index {} and/or {} in `{}` does not lie on a character \
             boundary",
            start,
            end,
            &s[..(s.len() - iter.as_str().len())]
        ))
    }
}

extern "C" fn from_utf8(thread: &Thread) -> Status {
    let mut context = thread.context();
    let value = StackFrame::current(&mut context.stack)[0];
    match value {
        Value::Array(array) => {
            match GcStr::from_utf8(array) {
                Ok(string) => {
                    let value = Value::String(string);
                    let result = context.alloc_with(
                        thread,
                        Def {
                            tag: 1,
                            elems: &[value],
                        },
                    );
                    match result {
                        Ok(data) => {
                            context.stack.push(Value::Data(data));
                            Status::Ok
                        }
                        Err(err) => {
                            let result: RuntimeResult<(), _> = RuntimeResult::Panic(err);
                            result.status_push(thread, &mut context)
                        }
                    }
                }
                Err(()) =>{
                    let err: StdResult<&str, ()> = Err(());
                    err.status_push(thread, &mut context)
                }
            }
        }
        _ => unreachable!(),
    }
}

fn char_at(s: &str, index: usize) -> RuntimeResult<char, String> {
    if s.is_char_boundary(index) {
        if let Some(c) = s[index..].chars().next() {
            return RuntimeResult::Return(c);
        }
    }
    let mut iter = s.chars();
    for _ in iter.by_ref().take(256) {}
    RuntimeResult::Panic(format!(
        "index {} in `{}` does not lie on a character boundary",
        index,
        &s[..(s.len() - iter.as_str().len())]
    ))
}

fn show_int(i: VmInt) -> String {
    format!("{}", i)
}

fn show_float(f: f64) -> String {
    format!("{}", f)
}

fn show_char(c: char) -> String {
    format!("{}", c)
}

extern "C" fn error(_: &Thread) -> Status {
    // We expect a string as an argument to this function but we only return Status::Error
    // and let the caller take care of printing the message
    Status::Error
}

pub fn load(vm: &Thread) -> Result<()> {
    use std::f64;
    use std::char;
    vm.define_global(
        "float",
        record!(
        digits => f64::DIGITS,
        epsilon => f64::EPSILON,
        infinity => f64::INFINITY,
        mantissa_digits => f64::MANTISSA_DIGITS,
        max_ => f64::MAX,
        max_10_exp => f64::MAX_10_EXP,
        max_exp => f64::MAX_EXP,
        min_ => f64::MIN,
        min_10_exp => f64::MIN_10_EXP,
        min_exp => f64::MIN_EXP,
        min_positive => f64::MIN_POSITIVE,
        nan => f64::NAN,
        neg_infinity => f64::NEG_INFINITY,
        e => f64::consts::E,
        pi => f64::consts::PI,
        radix => f64::RADIX,
        is_nan => primitive!(1 f64::is_nan),
        is_infinite => primitive!(1 f64::is_infinite),
        is_finite => primitive!(1 f64::is_finite),
        is_normal => primitive!(1 f64::is_normal),
        floor => primitive!(1 f64::floor),
        ceil => primitive!(1 f64::ceil),
        round => primitive!(1 f64::round),
        trunc => primitive!(1 f64::trunc),
        fract => primitive!(1 f64::fract),
        abs => primitive!(1 f64::abs),
        signum => primitive!(1 f64::signum),
        is_sign_positive => primitive!(1 f64::is_sign_positive),
        is_sign_negative => primitive!(1 f64::is_sign_negative),
        mul_add => primitive!(3 f64::mul_add),
        recip => primitive!(1 f64::recip),
        powi => primitive!(2 f64::powi),
        powf => primitive!(2 f64::powf),
        sqrt => primitive!(1 f64::sqrt),
        exp => primitive!(1 f64::exp),
        exp2 => primitive!(1 f64::exp2),
        ln => primitive!(1 f64::ln),
        log2 => primitive!(1 f64::log2),
        log10 => primitive!(1 f64::log10),
        to_degrees => primitive!(1 f64::to_degrees),
        to_radians => primitive!(1 f64::to_radians),
        max => primitive!(2 f64::max),
        min => primitive!(2 f64::min),
        cbrt => primitive!(1 f64::cbrt),
        hypot => primitive!(2 f64::hypot),
        sin => primitive!(1 f64::sin),
        cos => primitive!(1 f64::cos),
        tan => primitive!(1 f64::tan),
        acos => primitive!(1 f64::acos),
        atan => primitive!(1 f64::atan),
        atan2 => primitive!(2 f64::atan2),
        sin_cos => primitive!(1 f64::sin_cos),
        exp_m1 => primitive!(1 f64::exp_m1),
        ln_1p => primitive!(1 f64::ln_1p),
        sinh => primitive!(1 f64::sinh),
        cosh => primitive!(1 f64::cosh),
        tanh => primitive!(1 f64::tanh),
        acosh => primitive!(1 f64::acosh),
        atanh => primitive!(1 f64::atanh)
    ),
    )?;
    vm.define_global(
        "int",
        record!(
        min_value => VmInt::min_value(),
        max_value => VmInt::max_value(),
        count_ones => primitive!(1 VmInt::count_ones),
        rotate_left => primitive!(2 VmInt::rotate_left),
        rotate_right => primitive!(2 VmInt::rotate_right),
        swap_bytes => primitive!(1 VmInt::swap_bytes),
        from_be => primitive!(1 VmInt::from_be),
        from_le => primitive!(1 VmInt::from_le),
        to_be => primitive!(1 VmInt::to_be),
        to_le => primitive!(1 VmInt::to_le),
        pow => primitive!(2 VmInt::pow),
        abs => primitive!(1 VmInt::abs),
        signum => primitive!(1 VmInt::signum),
        is_positive => primitive!(1 VmInt::is_positive),
        is_negative => primitive!(1 VmInt::is_negative)
    ),
    )?;
    vm.define_global(
        "array",
        record!(
        length => primitive!(1 prim::array_length),
        index => primitive!(2 prim::array_index),
        append => primitive!(2 prim::array_append)
    ),
    )?;

    vm.define_global("string_prim",
                       record!(
        length => primitive!(1 str::len),
        is_empty => primitive!(1 str::is_empty),
        split_at => primitive!(2 str::split_at),
        find => primitive!(2 str::find::<&str>),
        rfind => primitive!(2 str::rfind::<&str>),
        starts_with => primitive!(2 str::starts_with::<&str>),
        ends_with => primitive!(2 str::ends_with::<&str>),
        trim => primitive!(1 str::trim),
        trim_left => primitive!(1 str::trim_left),
        trim_right => primitive!(1 str::trim_right),
        compare => primitive!(2 str::cmp),
        append => primitive!(2 prim::string_append),
        eq => primitive!(2 <str as PartialEq>::eq),
        slice => primitive!(3 prim::string_slice),
        from_utf8 => primitive::<fn(Vec<u8>) -> StdResult<String, ()>>("from_utf8", prim::from_utf8),
        char_at => primitive!(2 prim::char_at),
        as_bytes => primitive!(1 str::as_bytes)
    ))?;
    vm.define_global(
        "char",
        record!(
        is_digit => primitive!(2 char::is_digit),
        to_digit => primitive!(2 char::to_digit),
        len_utf8 => primitive!(1 char::len_utf8),
        len_utf16 => primitive!(1 char::len_utf16),
        is_alphabetic => primitive!(1 char::is_alphabetic),
        is_lowercase => primitive!(1 char::is_lowercase),
        is_uppercase => primitive!(1 char::is_uppercase),
        is_whitespace => primitive!(1 char::is_whitespace),
        is_alphanumeric => primitive!(1 char::is_alphanumeric),
        is_control => primitive!(1 char::is_control),
        is_numeric => primitive!(1 char::is_numeric)
    ),
    )?;
    vm.define_global(
        "prim",
        record!(
        show_Int => primitive!(1 prim::show_int),
        show_Float => primitive!(1 prim::show_float),
        show_Char => primitive!(1 prim::show_char)
    ),
    )?;

    vm.define_global(
        "#error",
        primitive::<fn(StdString) -> Generic<A>>("#error", prim::error),
    )?;
    vm.define_global(
        "error",
        primitive::<fn(StdString) -> Generic<A>>("error", prim::error),
    )?;

    ::lazy::load(vm)?;
    ::reference::load(vm)?;
    Ok(())
}
<commit_msg>feat(vm): Add parse functions for floats and integers<commit_after>//! Module containing functions for interacting with gluon's primitive types.
use std::result::Result as StdResult;
use std::string::String as StdString;
use std::str::FromStr;

use {Variants, Error};
use primitives as prim;
use api::{generic, Generic, Getable, AsyncPushable, Array, RuntimeResult, primitive, WithVM};
use api::generic::A;
use gc::{Gc, Traverseable, DataDef, WriteOnly};
use Result;
use vm::{Thread, Status};
use value::{Def, GcStr, Repr, Value, ValueArray};
use stack::StackFrame;
use thread::ThreadInternal;
use types::VmInt;

fn array_length(array: Array<generic::A>) -> VmInt {
    array.len() as VmInt
}

fn array_index<'vm>(
    array: Array<'vm, Generic<generic::A>>,
    index: VmInt,
) -> RuntimeResult<Generic<generic::A>, String> {
    match array.get(index) {
        Some(value) => RuntimeResult::Return(value),
        None => RuntimeResult::Panic(format!("Index {} is out of range", index)),
    }
}

fn array_append<'vm>(
    lhs: Array<'vm, Generic<generic::A>>,
    rhs: Array<'vm, Generic<generic::A>>,
) -> RuntimeResult<Array<'vm, Generic<generic::A>>, Error> {
    struct Append<'b> {
        lhs: &'b ValueArray,
        rhs: &'b ValueArray,
    }

    impl<'b> Append<'b> {
        fn repr(&self) -> Repr {
            // Empty arrays don't have the correct representation set so choose the representation
            // of `rhs` if it is empty. (And if both are empty the representation does not matter).
            if self.lhs.len() == 0 {
                self.rhs.repr()
            } else {
                self.lhs.repr()
            }
        }
    }

    impl<'b> Traverseable for Append<'b> {
        fn traverse(&self, gc: &mut Gc) {
            self.lhs.traverse(gc);
            self.rhs.traverse(gc);
        }
    }

    unsafe impl<'b> DataDef for Append<'b> {
        type Value = ValueArray;
        fn size(&self) -> usize {
            let len = self.lhs.len() + self.rhs.len();
            ValueArray::size_of(self.repr(), len)
        }
        fn initialize<'w>(self, mut result: WriteOnly<'w, ValueArray>) -> &'w mut ValueArray {
            unsafe {
                let result = &mut *result.as_mut_ptr();
                result.set_repr(self.repr());
                result.initialize(self.lhs.iter().chain(self.rhs.iter()));
                result
            }
        }
    }
    let vm = lhs.vm();
    let value = {
        let mut context = vm.context();
        let result = context.alloc(Append {
            lhs: lhs.get_value_array(),
            rhs: rhs.get_value_array(),
        });
        match result {
            Ok(x) => x,
            Err(err) => return RuntimeResult::Panic(err),
        }
    };
    unsafe {
        RuntimeResult::Return(
            Getable::from_value(lhs.vm(), Variants::new(&Value::Array(value))).expect("Array"),
        )
    }
}

fn string_append(lhs: WithVM<&str>, rhs: &str) -> RuntimeResult<String, Error> {
    struct StrAppend<'b> {
        lhs: &'b str,
        rhs: &'b str,
    }

    impl<'b> Traverseable for StrAppend<'b> {}

    unsafe impl<'b> DataDef for StrAppend<'b> {
        type Value = ValueArray;
        fn size(&self) -> usize {
            use std::mem::size_of;
            size_of::<ValueArray>() + (self.lhs.len() + self.rhs.len()) * size_of::<u8>()
        }
        fn initialize<'w>(self, mut result: WriteOnly<'w, ValueArray>) -> &'w mut ValueArray {
            use value::Repr;
            unsafe {
                let result = &mut *result.as_mut_ptr();
                result.set_repr(Repr::Byte);
                result.unsafe_array_mut::<u8>().initialize(
                    self.lhs
                        .as_bytes()
                        .iter()
                        .chain(self.rhs.as_bytes())
                        .cloned(),
                );
                result
            }
        }
    }

    let vm = lhs.vm;
    let lhs = lhs.value;
    unsafe {
        let mut context = vm.context();
        let result = context.alloc(StrAppend { lhs: lhs, rhs: rhs });
        let value = match result {
            Ok(x) => GcStr::from_utf8_unchecked(x),
            Err(err) => return RuntimeResult::Panic(err),
        };
        RuntimeResult::Return(
            Getable::from_value(vm, Variants::new(&Value::String(value))).expect("Array"),
        )
    }
}

fn string_slice(s: &str, start: usize, end: usize) -> RuntimeResult<&str, String> {
    if s.is_char_boundary(start) && s.is_char_boundary(end) {
        RuntimeResult::Return(&s[start..end])
    } else {
        // Limit the amount of characters to print in the error message
        let mut iter = s.chars();
        for _ in iter.by_ref().take(256) {}
        RuntimeResult::Panic(format!(
            "index {} and/or {} in `{}` does not lie on a character \
             boundary",
            start,
            end,
            &s[..(s.len() - iter.as_str().len())]
        ))
    }
}

extern "C" fn from_utf8(thread: &Thread) -> Status {
    let mut context = thread.context();
    let value = StackFrame::current(&mut context.stack)[0];
    match value {
        Value::Array(array) => {
            match GcStr::from_utf8(array) {
                Ok(string) => {
                    let value = Value::String(string);
                    let result = context.alloc_with(
                        thread,
                        Def {
                            tag: 1,
                            elems: &[value],
                        },
                    );
                    match result {
                        Ok(data) => {
                            context.stack.push(Value::Data(data));
                            Status::Ok
                        }
                        Err(err) => {
                            let result: RuntimeResult<(), _> = RuntimeResult::Panic(err);
                            result.status_push(thread, &mut context)
                        }
                    }
                }
                Err(()) =>{
                    let err: StdResult<&str, ()> = Err(());
                    err.status_push(thread, &mut context)
                }
            }
        }
        _ => unreachable!(),
    }
}

fn char_at(s: &str, index: usize) -> RuntimeResult<char, String> {
    if s.is_char_boundary(index) {
        if let Some(c) = s[index..].chars().next() {
            return RuntimeResult::Return(c);
        }
    }
    let mut iter = s.chars();
    for _ in iter.by_ref().take(256) {}
    RuntimeResult::Panic(format!(
        "index {} in `{}` does not lie on a character boundary",
        index,
        &s[..(s.len() - iter.as_str().len())]
    ))
}

fn parse<T>(s: &str) -> StdResult<T, ()>
    where T: FromStr,
{
    s.parse().map_err(|_| ())
}

fn show_int(i: VmInt) -> String {
    format!("{}", i)
}

fn show_float(f: f64) -> String {
    format!("{}", f)
}

fn show_char(c: char) -> String {
    format!("{}", c)
}

extern "C" fn error(_: &Thread) -> Status {
    // We expect a string as an argument to this function but we only return Status::Error
    // and let the caller take care of printing the message
    Status::Error
}

pub fn load(vm: &Thread) -> Result<()> {
    use std::f64;
    use std::char;
    vm.define_global(
        "float",
        record!(
        digits => f64::DIGITS,
        epsilon => f64::EPSILON,
        infinity => f64::INFINITY,
        mantissa_digits => f64::MANTISSA_DIGITS,
        max_ => f64::MAX,
        max_10_exp => f64::MAX_10_EXP,
        max_exp => f64::MAX_EXP,
        min_ => f64::MIN,
        min_10_exp => f64::MIN_10_EXP,
        min_exp => f64::MIN_EXP,
        min_positive => f64::MIN_POSITIVE,
        nan => f64::NAN,
        neg_infinity => f64::NEG_INFINITY,
        e => f64::consts::E,
        pi => f64::consts::PI,
        radix => f64::RADIX,
        is_nan => primitive!(1 f64::is_nan),
        is_infinite => primitive!(1 f64::is_infinite),
        is_finite => primitive!(1 f64::is_finite),
        is_normal => primitive!(1 f64::is_normal),
        floor => primitive!(1 f64::floor),
        ceil => primitive!(1 f64::ceil),
        round => primitive!(1 f64::round),
        trunc => primitive!(1 f64::trunc),
        fract => primitive!(1 f64::fract),
        abs => primitive!(1 f64::abs),
        signum => primitive!(1 f64::signum),
        is_sign_positive => primitive!(1 f64::is_sign_positive),
        is_sign_negative => primitive!(1 f64::is_sign_negative),
        mul_add => primitive!(3 f64::mul_add),
        recip => primitive!(1 f64::recip),
        powi => primitive!(2 f64::powi),
        powf => primitive!(2 f64::powf),
        sqrt => primitive!(1 f64::sqrt),
        exp => primitive!(1 f64::exp),
        exp2 => primitive!(1 f64::exp2),
        ln => primitive!(1 f64::ln),
        log2 => primitive!(1 f64::log2),
        log10 => primitive!(1 f64::log10),
        to_degrees => primitive!(1 f64::to_degrees),
        to_radians => primitive!(1 f64::to_radians),
        max => primitive!(2 f64::max),
        min => primitive!(2 f64::min),
        cbrt => primitive!(1 f64::cbrt),
        hypot => primitive!(2 f64::hypot),
        sin => primitive!(1 f64::sin),
        cos => primitive!(1 f64::cos),
        tan => primitive!(1 f64::tan),
        acos => primitive!(1 f64::acos),
        atan => primitive!(1 f64::atan),
        atan2 => primitive!(2 f64::atan2),
        sin_cos => primitive!(1 f64::sin_cos),
        exp_m1 => primitive!(1 f64::exp_m1),
        ln_1p => primitive!(1 f64::ln_1p),
        sinh => primitive!(1 f64::sinh),
        cosh => primitive!(1 f64::cosh),
        tanh => primitive!(1 f64::tanh),
        acosh => primitive!(1 f64::acosh),
        atanh => primitive!(1 f64::atanh),
        parse => primitive!(1 parse::<f64>)
    ))?;
    vm.define_global("int",
        record!(
        min_value => VmInt::min_value(),
        max_value => VmInt::max_value(),
        count_ones => primitive!(1 VmInt::count_ones),
        rotate_left => primitive!(2 VmInt::rotate_left),
        rotate_right => primitive!(2 VmInt::rotate_right),
        swap_bytes => primitive!(1 VmInt::swap_bytes),
        from_be => primitive!(1 VmInt::from_be),
        from_le => primitive!(1 VmInt::from_le),
        to_be => primitive!(1 VmInt::to_be),
        to_le => primitive!(1 VmInt::to_le),
        pow => primitive!(2 VmInt::pow),
        abs => primitive!(1 VmInt::abs),
        signum => primitive!(1 VmInt::signum),
        is_positive => primitive!(1 VmInt::is_positive),
        is_negative => primitive!(1 VmInt::is_negative),
        parse => primitive!(1 parse::<VmInt>)
    ))?;
    vm.define_global("array",
        record!(
        length => primitive!(1 prim::array_length),
        index => primitive!(2 prim::array_index),
        append => primitive!(2 prim::array_append)
    ),
    )?;

    vm.define_global("string_prim",
                       record!(
        length => primitive!(1 str::len),
        is_empty => primitive!(1 str::is_empty),
        split_at => primitive!(2 str::split_at),
        find => primitive!(2 str::find::<&str>),
        rfind => primitive!(2 str::rfind::<&str>),
        starts_with => primitive!(2 str::starts_with::<&str>),
        ends_with => primitive!(2 str::ends_with::<&str>),
        trim => primitive!(1 str::trim),
        trim_left => primitive!(1 str::trim_left),
        trim_right => primitive!(1 str::trim_right),
        compare => primitive!(2 str::cmp),
        append => primitive!(2 prim::string_append),
        eq => primitive!(2 <str as PartialEq>::eq),
        slice => primitive!(3 prim::string_slice),
        from_utf8 => primitive::<fn(Vec<u8>) -> StdResult<String, ()>>("from_utf8", prim::from_utf8),
        char_at => primitive!(2 prim::char_at),
        as_bytes => primitive!(1 str::as_bytes)
    ))?;
    vm.define_global(
        "char",
        record!(
        is_digit => primitive!(2 char::is_digit),
        to_digit => primitive!(2 char::to_digit),
        len_utf8 => primitive!(1 char::len_utf8),
        len_utf16 => primitive!(1 char::len_utf16),
        is_alphabetic => primitive!(1 char::is_alphabetic),
        is_lowercase => primitive!(1 char::is_lowercase),
        is_uppercase => primitive!(1 char::is_uppercase),
        is_whitespace => primitive!(1 char::is_whitespace),
        is_alphanumeric => primitive!(1 char::is_alphanumeric),
        is_control => primitive!(1 char::is_control),
        is_numeric => primitive!(1 char::is_numeric)
    ),
    )?;
    vm.define_global(
        "prim",
        record!(
        show_Int => primitive!(1 prim::show_int),
        show_Float => primitive!(1 prim::show_float),
        show_Char => primitive!(1 prim::show_char)
    ),
    )?;

    vm.define_global(
        "#error",
        primitive::<fn(StdString) -> Generic<A>>("#error", prim::error),
    )?;
    vm.define_global(
        "error",
        primitive::<fn(StdString) -> Generic<A>>("error", prim::error),
    )?;

    ::lazy::load(vm)?;
    ::reference::load(vm)?;
    Ok(())
}
<|endoftext|>