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

use gpu_cache::GpuCacheHandle;
use internal_types::HardwareCompositeOp;
use mask_cache::MaskCacheInfo;
use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
use std::{cmp, f32, i32, mem, usize};
use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind, StackingContextIndex};
use api::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{FilterOp, MixBlendMode};

const FLOATS_PER_RENDER_TASK_INFO: usize = 12;

#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub struct RenderTaskIndex(pub usize);

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum RenderTaskKey {
    /// Draw this primitive to a cache target.
    CachePrimitive(PrimitiveCacheKey),
    /// Draw the alpha mask for a primitive.
    CacheMask(MaskCacheKey),
    /// Apply a vertical blur pass of given radius for this primitive.
    VerticalBlur(i32, PrimitiveIndex),
    /// Apply a horizontal blur pass of given radius for this primitive.
    HorizontalBlur(i32, PrimitiveIndex),
    /// Allocate a block of space in target for framebuffer copy.
    CopyFramebuffer(StackingContextIndex),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum MaskCacheKey {
    Primitive(PrimitiveIndex),
    ClipNode(ClipId),
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RenderTaskId {
    Static(RenderTaskIndex),
    Dynamic(RenderTaskKey),
}

#[derive(Debug, Clone)]
pub enum RenderTaskLocation {
    Fixed,
    Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
}

#[derive(Debug, Clone)]
pub enum AlphaRenderItem {
    Primitive(Option<ClipScrollGroupIndex>, PrimitiveIndex, i32),
    Blend(StackingContextIndex, RenderTaskId, FilterOp, i32),
    Composite(StackingContextIndex, RenderTaskId, RenderTaskId, MixBlendMode, i32),
    SplitComposite(StackingContextIndex, RenderTaskId, GpuCacheHandle, i32),
    HardwareComposite(StackingContextIndex, RenderTaskId, HardwareCompositeOp, i32),
}

#[derive(Debug, Clone)]
pub struct AlphaRenderTask {
    screen_origin: DeviceIntPoint,
    pub items: Vec<AlphaRenderItem>,
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub enum MaskSegment {
    // This must match the SEGMENT_ values in clip_shared.glsl!
    All = 0,
    TopLeftCorner,
    TopRightCorner,
    BottomLeftCorner,
    BottomRightCorner,
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub enum MaskGeometryKind {
    Default,        // Draw the entire rect
    CornersOnly,    // Draw the corners (simple axis aligned mask)
    // TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect)
}

pub type ClipWorkItem = (PackedLayerIndex, MaskCacheInfo);

#[derive(Debug, Clone)]
pub struct CacheMaskTask {
    actual_rect: DeviceIntRect,
    inner_rect: DeviceIntRect,
    pub clips: Vec<ClipWorkItem>,
    pub geometry_kind: MaskGeometryKind,
}

#[derive(Debug, Clone)]
pub struct RenderTaskData {
    pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
}

impl RenderTaskData {
    pub fn empty() -> RenderTaskData {
        RenderTaskData {
            data: unsafe { mem::uninitialized() }
        }
    }
}

impl Default for RenderTaskData {
    fn default() -> RenderTaskData {
        RenderTaskData {
            data: unsafe { mem::uninitialized() },
        }
    }
}

#[derive(Debug, Clone)]
pub enum RenderTaskKind {
    Alpha(AlphaRenderTask),
    CachePrimitive(PrimitiveIndex),
    CacheMask(CacheMaskTask),
    VerticalBlur(DeviceIntLength, PrimitiveIndex),
    HorizontalBlur(DeviceIntLength, PrimitiveIndex),
    Readback(DeviceIntRect),
}

// TODO(gw): Consider storing these in a separate array and having
//           primitives hold indices - this could avoid cloning
//           when adding them as child tasks to tiles.
#[derive(Debug, Clone)]
pub struct RenderTask {
    pub id: RenderTaskId,
    pub location: RenderTaskLocation,
    pub children: Vec<RenderTask>,
    pub kind: RenderTaskKind,
}

impl RenderTask {
    pub fn new_alpha_batch(task_index: RenderTaskIndex,
                           screen_origin: DeviceIntPoint,
                           location: RenderTaskLocation) -> RenderTask {
        RenderTask {
            id: RenderTaskId::Static(task_index),
            children: Vec::new(),
            location,
            kind: RenderTaskKind::Alpha(AlphaRenderTask {
                screen_origin,
                items: Vec::new(),
            }),
        }
    }

    pub fn new_dynamic_alpha_batch(task_index: RenderTaskIndex,
                                   rect: &DeviceIntRect) -> RenderTask {
        let location = RenderTaskLocation::Dynamic(None, rect.size);
        Self::new_alpha_batch(task_index, rect.origin, location)
    }

    pub fn new_prim_cache(key: PrimitiveCacheKey,
                          size: DeviceIntSize,
                          prim_index: PrimitiveIndex) -> RenderTask {
        RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::CachePrimitive(key)),
            children: Vec::new(),
            location: RenderTaskLocation::Dynamic(None, size),
            kind: RenderTaskKind::CachePrimitive(prim_index),
        }
    }

    pub fn new_readback(key: StackingContextIndex,
                    screen_rect: DeviceIntRect) -> RenderTask {
        RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::CopyFramebuffer(key)),
            children: Vec::new(),
            location: RenderTaskLocation::Dynamic(None, screen_rect.size),
            kind: RenderTaskKind::Readback(screen_rect),
        }
    }

    pub fn new_mask(task_rect: DeviceIntRect,
                    mask_key: MaskCacheKey,
                    raw_clips: &[ClipWorkItem],
                    extra_clip: Option<ClipWorkItem>)
                    -> Option<RenderTask> {
        /// Filter out all the clip instances that don't contribute to the result
        let mut inner_rect = Some(task_rect);
        let clips: Vec<_> = raw_clips.iter()
                                     .chain(extra_clip.iter())
                                     .filter(|&&(_, ref clip_info)| {
            match clip_info.bounds.inner {
                Some(ref inner) if !inner.device_rect.is_empty() => {
                    inner_rect = inner_rect.and_then(|r| r.intersection(&inner.device_rect));
                    !inner.device_rect.contains_rect(&task_rect)
                }
                _ => {
                    inner_rect = None;
                    true
                }
            }
        }).cloned().collect();

        // Nothing to do, all clips are irrelevant for this case
        if clips.is_empty() {
            return None
        }

        // TODO(gw): This optimization is very conservative for now.
        //           For now, only draw optimized geometry if it is
        //           a single aligned rect mask with rounded corners.
        //           In the future, we'll expand this to handle the
        //           more complex types of clip mask geometry.
        let mut geometry_kind = MaskGeometryKind::Default;
        if inner_rect.is_some() && clips.len() == 1 {
            let (_, ref info) = clips[0];
            if info.border_corners.is_empty() &&
               info.image.is_none() &&
               info.complex_clip_range.get_count() == 1 &&
               info.layer_clip_range.get_count() == 0 {
                geometry_kind = MaskGeometryKind::CornersOnly;
            }
        }

        Some(RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(mask_key)),
            children: Vec::new(),
            location: RenderTaskLocation::Dynamic(None, task_rect.size),
            kind: RenderTaskKind::CacheMask(CacheMaskTask {
                actual_rect: task_rect,
                inner_rect: inner_rect.unwrap_or(DeviceIntRect::zero()),
                clips,
                geometry_kind,
            }),
        })
    }

    // Construct a render task to apply a blur to a primitive. For now,
    // this is only used for text runs, but we can probably extend this
    // to handle general blurs to any render task in the future.
    // The render task chain that is constructed looks like:
    //
    //    PrimitiveCacheTask: Draw the text run.
    //           ^
    //           |
    //    VerticalBlurTask: Apply the separable vertical blur to the primitive.
    //           ^
    //           |
    //    HorizontalBlurTask: Apply the separable horizontal blur to the vertical blur.
    //           |
    //           +---- This is stored as the input task to the primitive shader.
    //
    pub fn new_blur(key: PrimitiveCacheKey,
                    size: DeviceIntSize,
                    blur_radius: DeviceIntLength,
                    prim_index: PrimitiveIndex) -> RenderTask {
        let prim_cache_task = RenderTask::new_prim_cache(key,
                                                         size,
                                                         prim_index);

        let blur_target_size = size + DeviceIntSize::new(2 * blur_radius.0,
                                                         2 * blur_radius.0);

        let blur_task_v = RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::VerticalBlur(blur_radius.0, prim_index)),
            children: vec![prim_cache_task],
            location: RenderTaskLocation::Dynamic(None, blur_target_size),
            kind: RenderTaskKind::VerticalBlur(blur_radius, prim_index),
        };

        let blur_task_h = RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::HorizontalBlur(blur_radius.0, prim_index)),
            children: vec![blur_task_v],
            location: RenderTaskLocation::Dynamic(None, blur_target_size),
            kind: RenderTaskKind::HorizontalBlur(blur_radius, prim_index),
        };

        blur_task_h
    }

    pub fn as_alpha_batch<'a>(&'a mut self) -> &'a mut AlphaRenderTask {
        match self.kind {
            RenderTaskKind::Alpha(ref mut task) => task,
            RenderTaskKind::CachePrimitive(..) |
            RenderTaskKind::CacheMask(..) |
            RenderTaskKind::VerticalBlur(..) |
            RenderTaskKind::Readback(..) |
            RenderTaskKind::HorizontalBlur(..) => unreachable!(),
        }
    }

    // Write (up to) 8 floats of data specific to the type
    // of render task that is provided to the GPU shaders
    // via a vertex texture.
    pub fn write_task_data(&self) -> RenderTaskData {
        let (target_rect, target_index) = self.get_target_rect();

        // NOTE: The ordering and layout of these structures are
        //       required to match both the GPU structures declared
        //       in prim_shared.glsl, and also the uses in submit_batch()
        //       in renderer.rs.
        // TODO(gw): Maybe there's a way to make this stuff a bit
        //           more type-safe. Although, it will always need
        //           to be kept in sync with the GLSL code anyway.

        match self.kind {
            RenderTaskKind::Alpha(ref task) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        target_rect.size.width as f32,
                        target_rect.size.height as f32,
                        task.screen_origin.x as f32,
                        task.screen_origin.y as f32,
                        target_index.0 as f32,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                    ],
                }
            }
            RenderTaskKind::CachePrimitive(..) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        target_rect.size.width as f32,
                        target_rect.size.height as f32,
                        target_index.0 as f32,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                    ],
                }
            }
            RenderTaskKind::CacheMask(ref task) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        (target_rect.origin.x + target_rect.size.width) as f32,
                        (target_rect.origin.y + target_rect.size.height) as f32,
                        task.actual_rect.origin.x as f32,
                        task.actual_rect.origin.y as f32,
                        target_index.0 as f32,
                        0.0,
                        task.inner_rect.origin.x as f32,
                        task.inner_rect.origin.y as f32,
                        (task.inner_rect.origin.x + task.inner_rect.size.width) as f32,
                        (task.inner_rect.origin.y + task.inner_rect.size.height) as f32,
                    ],
                }
            }
            RenderTaskKind::VerticalBlur(blur_radius, _) |
            RenderTaskKind::HorizontalBlur(blur_radius, _) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        target_rect.size.width as f32,
                        target_rect.size.height as f32,
                        target_index.0 as f32,
                        blur_radius.0 as f32,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                    ]
                }
            }
            RenderTaskKind::Readback(..) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        target_rect.size.width as f32,
                        target_rect.size.height as f32,
                        target_index.0 as f32,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                    ]
                }
            }
        }
    }

    fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
        match self.location {
            RenderTaskLocation::Fixed => {
                (DeviceIntRect::zero(), RenderTargetIndex(0))
            },
            RenderTaskLocation::Dynamic(origin_and_target_index, size) => {
                let (origin, target_index) = origin_and_target_index.expect("Should have been allocated by now!");
                (DeviceIntRect::new(origin, size), target_index)
            }
        }
    }

    pub fn assign_to_passes(mut self, pass_index: usize, passes: &mut Vec<RenderPass>) {
        for child in self.children.drain(..) {
            child.assign_to_passes(pass_index - 1,
                                   passes);
        }

        // Sanity check - can be relaxed if needed
        match self.location {
            RenderTaskLocation::Fixed => {
                debug_assert!(pass_index == passes.len() - 1);
            }
            RenderTaskLocation::Dynamic(..) => {
                debug_assert!(pass_index < passes.len() - 1);
            }
        }

        let pass = &mut passes[pass_index];
        pass.add_render_task(self);
    }

    pub fn max_depth(&self, depth: usize, max_depth: &mut usize) {
        let depth = depth + 1;
        *max_depth = cmp::max(*max_depth, depth);
        for child in &self.children {
            child.max_depth(depth, max_depth);
        }
    }

    pub fn target_kind(&self) -> RenderTargetKind {
        match self.kind {
            RenderTaskKind::Alpha(..) |
            RenderTaskKind::CachePrimitive(..) |
            RenderTaskKind::VerticalBlur(..) |
            RenderTaskKind::Readback(..) |
            RenderTaskKind::HorizontalBlur(..) => RenderTargetKind::Color,
            RenderTaskKind::CacheMask(..) => RenderTargetKind::Alpha,
        }
    }
}
<commit_msg>Auto merge of #1549 - Manishearth:doc-cmt, r=emilio<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use gpu_cache::GpuCacheHandle;
use internal_types::HardwareCompositeOp;
use mask_cache::MaskCacheInfo;
use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
use std::{cmp, f32, i32, mem, usize};
use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind, StackingContextIndex};
use api::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{FilterOp, MixBlendMode};

const FLOATS_PER_RENDER_TASK_INFO: usize = 12;

#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub struct RenderTaskIndex(pub usize);

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum RenderTaskKey {
    /// Draw this primitive to a cache target.
    CachePrimitive(PrimitiveCacheKey),
    /// Draw the alpha mask for a primitive.
    CacheMask(MaskCacheKey),
    /// Apply a vertical blur pass of given radius for this primitive.
    VerticalBlur(i32, PrimitiveIndex),
    /// Apply a horizontal blur pass of given radius for this primitive.
    HorizontalBlur(i32, PrimitiveIndex),
    /// Allocate a block of space in target for framebuffer copy.
    CopyFramebuffer(StackingContextIndex),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum MaskCacheKey {
    Primitive(PrimitiveIndex),
    ClipNode(ClipId),
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RenderTaskId {
    Static(RenderTaskIndex),
    Dynamic(RenderTaskKey),
}

#[derive(Debug, Clone)]
pub enum RenderTaskLocation {
    Fixed,
    Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
}

#[derive(Debug, Clone)]
pub enum AlphaRenderItem {
    Primitive(Option<ClipScrollGroupIndex>, PrimitiveIndex, i32),
    Blend(StackingContextIndex, RenderTaskId, FilterOp, i32),
    Composite(StackingContextIndex, RenderTaskId, RenderTaskId, MixBlendMode, i32),
    SplitComposite(StackingContextIndex, RenderTaskId, GpuCacheHandle, i32),
    HardwareComposite(StackingContextIndex, RenderTaskId, HardwareCompositeOp, i32),
}

#[derive(Debug, Clone)]
pub struct AlphaRenderTask {
    screen_origin: DeviceIntPoint,
    pub items: Vec<AlphaRenderItem>,
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub enum MaskSegment {
    // This must match the SEGMENT_ values in clip_shared.glsl!
    All = 0,
    TopLeftCorner,
    TopRightCorner,
    BottomLeftCorner,
    BottomRightCorner,
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub enum MaskGeometryKind {
    Default,        // Draw the entire rect
    CornersOnly,    // Draw the corners (simple axis aligned mask)
    // TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect)
}

pub type ClipWorkItem = (PackedLayerIndex, MaskCacheInfo);

#[derive(Debug, Clone)]
pub struct CacheMaskTask {
    actual_rect: DeviceIntRect,
    inner_rect: DeviceIntRect,
    pub clips: Vec<ClipWorkItem>,
    pub geometry_kind: MaskGeometryKind,
}

#[derive(Debug, Clone)]
pub struct RenderTaskData {
    pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
}

impl RenderTaskData {
    pub fn empty() -> RenderTaskData {
        RenderTaskData {
            data: unsafe { mem::uninitialized() }
        }
    }
}

impl Default for RenderTaskData {
    fn default() -> RenderTaskData {
        RenderTaskData {
            data: unsafe { mem::uninitialized() },
        }
    }
}

#[derive(Debug, Clone)]
pub enum RenderTaskKind {
    Alpha(AlphaRenderTask),
    CachePrimitive(PrimitiveIndex),
    CacheMask(CacheMaskTask),
    VerticalBlur(DeviceIntLength, PrimitiveIndex),
    HorizontalBlur(DeviceIntLength, PrimitiveIndex),
    Readback(DeviceIntRect),
}

// TODO(gw): Consider storing these in a separate array and having
//           primitives hold indices - this could avoid cloning
//           when adding them as child tasks to tiles.
#[derive(Debug, Clone)]
pub struct RenderTask {
    pub id: RenderTaskId,
    pub location: RenderTaskLocation,
    pub children: Vec<RenderTask>,
    pub kind: RenderTaskKind,
}

impl RenderTask {
    pub fn new_alpha_batch(task_index: RenderTaskIndex,
                           screen_origin: DeviceIntPoint,
                           location: RenderTaskLocation) -> RenderTask {
        RenderTask {
            id: RenderTaskId::Static(task_index),
            children: Vec::new(),
            location,
            kind: RenderTaskKind::Alpha(AlphaRenderTask {
                screen_origin,
                items: Vec::new(),
            }),
        }
    }

    pub fn new_dynamic_alpha_batch(task_index: RenderTaskIndex,
                                   rect: &DeviceIntRect) -> RenderTask {
        let location = RenderTaskLocation::Dynamic(None, rect.size);
        Self::new_alpha_batch(task_index, rect.origin, location)
    }

    pub fn new_prim_cache(key: PrimitiveCacheKey,
                          size: DeviceIntSize,
                          prim_index: PrimitiveIndex) -> RenderTask {
        RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::CachePrimitive(key)),
            children: Vec::new(),
            location: RenderTaskLocation::Dynamic(None, size),
            kind: RenderTaskKind::CachePrimitive(prim_index),
        }
    }

    pub fn new_readback(key: StackingContextIndex,
                    screen_rect: DeviceIntRect) -> RenderTask {
        RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::CopyFramebuffer(key)),
            children: Vec::new(),
            location: RenderTaskLocation::Dynamic(None, screen_rect.size),
            kind: RenderTaskKind::Readback(screen_rect),
        }
    }

    pub fn new_mask(task_rect: DeviceIntRect,
                    mask_key: MaskCacheKey,
                    raw_clips: &[ClipWorkItem],
                    extra_clip: Option<ClipWorkItem>)
                    -> Option<RenderTask> {
        // Filter out all the clip instances that don't contribute to the result
        let mut inner_rect = Some(task_rect);
        let clips: Vec<_> = raw_clips.iter()
                                     .chain(extra_clip.iter())
                                     .filter(|&&(_, ref clip_info)| {
            match clip_info.bounds.inner {
                Some(ref inner) if !inner.device_rect.is_empty() => {
                    inner_rect = inner_rect.and_then(|r| r.intersection(&inner.device_rect));
                    !inner.device_rect.contains_rect(&task_rect)
                }
                _ => {
                    inner_rect = None;
                    true
                }
            }
        }).cloned().collect();

        // Nothing to do, all clips are irrelevant for this case
        if clips.is_empty() {
            return None
        }

        // TODO(gw): This optimization is very conservative for now.
        //           For now, only draw optimized geometry if it is
        //           a single aligned rect mask with rounded corners.
        //           In the future, we'll expand this to handle the
        //           more complex types of clip mask geometry.
        let mut geometry_kind = MaskGeometryKind::Default;
        if inner_rect.is_some() && clips.len() == 1 {
            let (_, ref info) = clips[0];
            if info.border_corners.is_empty() &&
               info.image.is_none() &&
               info.complex_clip_range.get_count() == 1 &&
               info.layer_clip_range.get_count() == 0 {
                geometry_kind = MaskGeometryKind::CornersOnly;
            }
        }

        Some(RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(mask_key)),
            children: Vec::new(),
            location: RenderTaskLocation::Dynamic(None, task_rect.size),
            kind: RenderTaskKind::CacheMask(CacheMaskTask {
                actual_rect: task_rect,
                inner_rect: inner_rect.unwrap_or(DeviceIntRect::zero()),
                clips,
                geometry_kind,
            }),
        })
    }

    // Construct a render task to apply a blur to a primitive. For now,
    // this is only used for text runs, but we can probably extend this
    // to handle general blurs to any render task in the future.
    // The render task chain that is constructed looks like:
    //
    //    PrimitiveCacheTask: Draw the text run.
    //           ^
    //           |
    //    VerticalBlurTask: Apply the separable vertical blur to the primitive.
    //           ^
    //           |
    //    HorizontalBlurTask: Apply the separable horizontal blur to the vertical blur.
    //           |
    //           +---- This is stored as the input task to the primitive shader.
    //
    pub fn new_blur(key: PrimitiveCacheKey,
                    size: DeviceIntSize,
                    blur_radius: DeviceIntLength,
                    prim_index: PrimitiveIndex) -> RenderTask {
        let prim_cache_task = RenderTask::new_prim_cache(key,
                                                         size,
                                                         prim_index);

        let blur_target_size = size + DeviceIntSize::new(2 * blur_radius.0,
                                                         2 * blur_radius.0);

        let blur_task_v = RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::VerticalBlur(blur_radius.0, prim_index)),
            children: vec![prim_cache_task],
            location: RenderTaskLocation::Dynamic(None, blur_target_size),
            kind: RenderTaskKind::VerticalBlur(blur_radius, prim_index),
        };

        let blur_task_h = RenderTask {
            id: RenderTaskId::Dynamic(RenderTaskKey::HorizontalBlur(blur_radius.0, prim_index)),
            children: vec![blur_task_v],
            location: RenderTaskLocation::Dynamic(None, blur_target_size),
            kind: RenderTaskKind::HorizontalBlur(blur_radius, prim_index),
        };

        blur_task_h
    }

    pub fn as_alpha_batch<'a>(&'a mut self) -> &'a mut AlphaRenderTask {
        match self.kind {
            RenderTaskKind::Alpha(ref mut task) => task,
            RenderTaskKind::CachePrimitive(..) |
            RenderTaskKind::CacheMask(..) |
            RenderTaskKind::VerticalBlur(..) |
            RenderTaskKind::Readback(..) |
            RenderTaskKind::HorizontalBlur(..) => unreachable!(),
        }
    }

    // Write (up to) 8 floats of data specific to the type
    // of render task that is provided to the GPU shaders
    // via a vertex texture.
    pub fn write_task_data(&self) -> RenderTaskData {
        let (target_rect, target_index) = self.get_target_rect();

        // NOTE: The ordering and layout of these structures are
        //       required to match both the GPU structures declared
        //       in prim_shared.glsl, and also the uses in submit_batch()
        //       in renderer.rs.
        // TODO(gw): Maybe there's a way to make this stuff a bit
        //           more type-safe. Although, it will always need
        //           to be kept in sync with the GLSL code anyway.

        match self.kind {
            RenderTaskKind::Alpha(ref task) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        target_rect.size.width as f32,
                        target_rect.size.height as f32,
                        task.screen_origin.x as f32,
                        task.screen_origin.y as f32,
                        target_index.0 as f32,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                    ],
                }
            }
            RenderTaskKind::CachePrimitive(..) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        target_rect.size.width as f32,
                        target_rect.size.height as f32,
                        target_index.0 as f32,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                    ],
                }
            }
            RenderTaskKind::CacheMask(ref task) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        (target_rect.origin.x + target_rect.size.width) as f32,
                        (target_rect.origin.y + target_rect.size.height) as f32,
                        task.actual_rect.origin.x as f32,
                        task.actual_rect.origin.y as f32,
                        target_index.0 as f32,
                        0.0,
                        task.inner_rect.origin.x as f32,
                        task.inner_rect.origin.y as f32,
                        (task.inner_rect.origin.x + task.inner_rect.size.width) as f32,
                        (task.inner_rect.origin.y + task.inner_rect.size.height) as f32,
                    ],
                }
            }
            RenderTaskKind::VerticalBlur(blur_radius, _) |
            RenderTaskKind::HorizontalBlur(blur_radius, _) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        target_rect.size.width as f32,
                        target_rect.size.height as f32,
                        target_index.0 as f32,
                        blur_radius.0 as f32,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                    ]
                }
            }
            RenderTaskKind::Readback(..) => {
                RenderTaskData {
                    data: [
                        target_rect.origin.x as f32,
                        target_rect.origin.y as f32,
                        target_rect.size.width as f32,
                        target_rect.size.height as f32,
                        target_index.0 as f32,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                        0.0,
                    ]
                }
            }
        }
    }

    fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
        match self.location {
            RenderTaskLocation::Fixed => {
                (DeviceIntRect::zero(), RenderTargetIndex(0))
            },
            RenderTaskLocation::Dynamic(origin_and_target_index, size) => {
                let (origin, target_index) = origin_and_target_index.expect("Should have been allocated by now!");
                (DeviceIntRect::new(origin, size), target_index)
            }
        }
    }

    pub fn assign_to_passes(mut self, pass_index: usize, passes: &mut Vec<RenderPass>) {
        for child in self.children.drain(..) {
            child.assign_to_passes(pass_index - 1,
                                   passes);
        }

        // Sanity check - can be relaxed if needed
        match self.location {
            RenderTaskLocation::Fixed => {
                debug_assert!(pass_index == passes.len() - 1);
            }
            RenderTaskLocation::Dynamic(..) => {
                debug_assert!(pass_index < passes.len() - 1);
            }
        }

        let pass = &mut passes[pass_index];
        pass.add_render_task(self);
    }

    pub fn max_depth(&self, depth: usize, max_depth: &mut usize) {
        let depth = depth + 1;
        *max_depth = cmp::max(*max_depth, depth);
        for child in &self.children {
            child.max_depth(depth, max_depth);
        }
    }

    pub fn target_kind(&self) -> RenderTargetKind {
        match self.kind {
            RenderTaskKind::Alpha(..) |
            RenderTaskKind::CachePrimitive(..) |
            RenderTaskKind::VerticalBlur(..) |
            RenderTaskKind::Readback(..) |
            RenderTaskKind::HorizontalBlur(..) => RenderTargetKind::Color,
            RenderTaskKind::CacheMask(..) => RenderTargetKind::Alpha,
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Write;
use std::env;

fn dlerror_is_mtsafe(target_os: &str) {
    match target_os {
        // Confirmed MT-safe:
        "linux"
        | "android"
        | "openbsd"
        | "macos"
        | "ios"
        | "solaris"
        | "redox"
        | "fuchsia" => {
            println!("cargo:rustc-cfg=mtsafe_dlerror");
        }
        // Confirmed not MT-safe:
        "freebsd"
        | "dragonfly"
        | "netbsd"
        | "bitrig"
        | "haiku" => {}
        // Unknown:
        _ => {}
    }
}

fn link_libraries(target_os: &str) {
    match target_os {
        "linux" | "android" => println!("cargo:rustc-link-lib=dl"),
        "freebsd" | "dragonfly" => println!("cargo:rustc-link-lib=c"),
        // netbsd claims dl* will be available to any dynamically linked binary, but I haven’t
        // found any libraries that have to be linked to on other platforms.
        // What happens if the executable is not linked up dynamically?
        "openbsd" | "bitrig" | "netbsd" | "macos" | "ios" => {}
        "solaris" => {}
        "haiku" => {}
        "redox" => {}
        "fuchsia" => {}
        // dependencies come with winapi
        "windows" => {}
        tos => {
            writeln!(::std::io::stderr(),
                     "Building for an unknown target_os=`{:?}`!\nPlease report an issue ",
                     tos).expect("could not report the error");
            ::std::process::exit(0xfc);
        }
    }
}

fn main() {
    match env::var("CARGO_CFG_TARGET_OS") {
        Ok(target_os) => {
            dlerror_is_mtsafe(&target_os);
            link_libraries(&target_os);
        }
        Err(e) => {
            writeln!(::std::io::stderr(),
                     "Unable to get target_os=`{}`!", e).expect("could not report the error");
            ::std::process::exit(0xfd);
        }
    }
}
<commit_msg>Add support for the illumos triple<commit_after>use std::io::Write;
use std::env;

fn dlerror_is_mtsafe(target_os: &str) {
    match target_os {
        // Confirmed MT-safe:
        "linux"
        | "android"
        | "openbsd"
        | "macos"
        | "ios"
        | "solaris"
        | "illumos"
        | "redox"
        | "fuchsia" => {
            println!("cargo:rustc-cfg=mtsafe_dlerror");
        }
        // Confirmed not MT-safe:
        "freebsd"
        | "dragonfly"
        | "netbsd"
        | "bitrig"
        | "haiku" => {}
        // Unknown:
        _ => {}
    }
}

fn link_libraries(target_os: &str) {
    match target_os {
        "linux" | "android" => println!("cargo:rustc-link-lib=dl"),
        "freebsd" | "dragonfly" => println!("cargo:rustc-link-lib=c"),
        // netbsd claims dl* will be available to any dynamically linked binary, but I haven’t
        // found any libraries that have to be linked to on other platforms.
        // What happens if the executable is not linked up dynamically?
        "openbsd" | "bitrig" | "netbsd" | "macos" | "ios" => {}
        "solaris" => {}
        "haiku" => {}
        "redox" => {}
        "fuchsia" => {}
        // dependencies come with winapi
        "windows" => {}
        tos => {
            writeln!(::std::io::stderr(),
                     "Building for an unknown target_os=`{:?}`!\nPlease report an issue ",
                     tos).expect("could not report the error");
            ::std::process::exit(0xfc);
        }
    }
}

fn main() {
    match env::var("CARGO_CFG_TARGET_OS") {
        Ok(target_os) => {
            dlerror_is_mtsafe(&target_os);
            link_libraries(&target_os);
        }
        Err(e) => {
            writeln!(::std::io::stderr(),
                     "Unable to get target_os=`{}`!", e).expect("could not report the error");
            ::std::process::exit(0xfd);
        }
    }
}
<|endoftext|>


<commit_before>fn main() {
    if let Ok(custom) = ::std::env::var("CUSTOM_LIBFUZZER_PATH") {
        let custom_lib_path = ::std::path::PathBuf::from(&custom);
        let custom_lib_dir = custom_lib_path.parent().unwrap().to_string_lossy();

        let custom_lib_name = custom_lib_path.file_stem().unwrap().to_string_lossy();
        let custom_lib_name = custom_lib_name.trim_start_matches("lib");

        println!("cargo:rustc-link-search=native={}", custom_lib_dir);
        println!("cargo:rustc-link-lib=static={}", custom_lib_name);
        /* FIXME: this is assuming a C++ fuzzer, but should be customizable */
        println!("cargo:rustc-link-lib=stdc++");
    } else {
        let mut build = cc::Build::new();
        let sources = ::std::fs::read_dir("libfuzzer")
            .expect("listable source directory")
            .map(|de| de.expect("file in directory").path())
            .filter(|p| p.extension().map(|ext| ext == "cpp") == Some(true))
            .collect::<Vec<_>>();
        for source in sources.iter() {
            build.file(source.to_str().unwrap());
        }
        build.flag("-std=c++11");
        build.flag("-fno-omit-frame-pointer");
        build.flag("-w");
        build.cpp(true);
        build.compile("libfuzzer.a");
    }
}
<commit_msg>Only rerun the build script if the C++ sources changed<commit_after>fn main() {
    if let Ok(custom) = ::std::env::var("CUSTOM_LIBFUZZER_PATH") {
        let custom_lib_path = ::std::path::PathBuf::from(&custom);
        let custom_lib_dir = custom_lib_path.parent().unwrap().to_string_lossy();

        let custom_lib_name = custom_lib_path.file_stem().unwrap().to_string_lossy();
        let custom_lib_name = custom_lib_name.trim_start_matches("lib");

        println!("cargo:rustc-link-search=native={}", custom_lib_dir);
        println!("cargo:rustc-link-lib=static={}", custom_lib_name);
        /* FIXME: this is assuming a C++ fuzzer, but should be customizable */
        println!("cargo:rustc-link-lib=stdc++");
    } else {
        let mut build = cc::Build::new();
        let sources = ::std::fs::read_dir("libfuzzer")
            .expect("listable source directory")
            .map(|de| de.expect("file in directory").path())
            .filter(|p| p.extension().map(|ext| ext == "cpp") == Some(true))
            .collect::<Vec<_>>();
        for source in sources.iter() {
            println!("cargo:rerun-if-changed={}", source.display());
            build.file(source.to_str().unwrap());
        }
        build.flag("-std=c++11");
        build.flag("-fno-omit-frame-pointer");
        build.flag("-w");
        build.cpp(true);
        build.compile("libfuzzer.a");
    }
}
<|endoftext|>


<commit_before>extern crate gcc;

use std::fs;
use std::io;
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::ffi::OsString;

trait CommandExt {
    fn execute(&mut self) -> io::Result<()>;
}

impl CommandExt for Command {
    /// Execute the command and return an error if it exited with a failure status.
    fn execute(&mut self) -> io::Result<()> {
        let status = try!(self.status());
        if status.success() {
            Ok(())
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
            \t{:?}\n\
            did not run successfully.", self)))
        }
    }
}

/// The command to build lua, with switches for different OSes.
fn build_lua(tooling: &gcc::Tool, dir: &Path) -> io::Result<()> {
    // calculate the Lua platform name
    let platform = match env::var("TARGET").unwrap().split('-').nth(2).unwrap() {
        "windows" => "mingw",
        "macos" => "macosx",
        "linux" => "linux",
        "freebsd" => "freebsd",
        "dragonfly" => "bsd",
        // fall back to the "generic" system
        _ => "generic",
    };

    // build the CC and MYCFLAGS parameters
    let mut cc = OsString::from("CC=");
    cc.push(tooling.path());
    let mut cflags = OsString::from("MYCFLAGS=");
    for arg in tooling.args() {
        cflags.push(arg);
        cflags.push(" ");
    }

    // call the makefile
    let mut command = Command::new("make");
    for &(ref key, ref val) in tooling.env() {
        command.env(key, val);
    }
    command.current_dir(dir)
        .arg(platform)
        .arg(cc)
        .arg(cflags)
        .execute()
}

/// The command to fetch a URL (e.g. with wget) specialized for different
/// OSes.
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos")))]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("wget").arg(url).current_dir(cwd).execute()
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("fetch").arg(url).current_dir(cwd).execute()
}

#[cfg(target_os = "macos")]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("curl").arg("-O").arg(url).current_dir(cwd).execute()
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() -> io::Result<()> {
    let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let tooling = gcc::Config::new().get_compiler();

    // Ensure the presence of liblua.a
    if !fs::metadata(build_dir.join("lua-5.3.0/src/liblua.a")).is_ok() {
        try!(fs::create_dir_all(&build_dir));

        // Download lua if it hasn't been already
        if !fs::metadata(build_dir.join("lua-5.3.0.tar.gz")).is_ok() {
            try!(fetch_in_dir("http://www.lua.org/ftp/lua-5.3.0.tar.gz", &build_dir));
            try!(Command::new("tar")
                .arg("xzf")
                .arg("lua-5.3.0.tar.gz")
                .current_dir(&build_dir)
                .execute());
        }
        // Compile lua
        try!(build_lua(&tooling, &build_dir.join("lua-5.3.0")));
    }

    // Ensure the presence of glue.rs
    if !fs::metadata(build_dir.join("glue.rs")).is_ok() {
        // Compile glue.c
        let glue = build_dir.join("glue");
        try!(Command::new("gcc")
            .arg("-I").arg(build_dir.join("lua-5.3.0/src"))
            .arg("src/glue/glue.c")
            .arg("-o").arg(&glue)
            .execute());
        try!(Command::new(glue)
            .arg(build_dir.join("glue.rs"))
            .execute());
    }

    // Output build information
    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}/lua-5.3.0/src", build_dir.to_str().unwrap());

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<commit_msg>Support building and linking against a local Lua source tarball.<commit_after>extern crate gcc;

use std::fs;
use std::io;
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::ffi::OsString;

trait CommandExt {
    fn execute(&mut self) -> io::Result<()>;
}

impl CommandExt for Command {
    /// Execute the command and return an error if it exited with a failure status.
    fn execute(&mut self) -> io::Result<()> {
        let status = try!(self.status());
        if status.success() {
            Ok(())
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
            \t{:?}\n\
            did not run successfully.", self)))
        }
    }
}

/// The command to build lua, with switches for different OSes.
fn build_lua(tooling: &gcc::Tool, dir: &Path) -> io::Result<()> {
    // calculate the Lua platform name
    let platform = match env::var("TARGET").unwrap().split('-').nth(2).unwrap() {
        "windows" => "mingw",
        "macos" => "macosx",
        "linux" => "linux",
        "freebsd" => "freebsd",
        "dragonfly" => "bsd",
        // fall back to the "generic" system
        _ => "generic",
    };

    // build the CC and MYCFLAGS parameters
    let mut cc = OsString::from("CC=");
    cc.push(tooling.path());
    let mut cflags = OsString::from("MYCFLAGS=");
    for arg in tooling.args() {
        cflags.push(arg);
        cflags.push(" ");
    }

    // call the makefile
    let mut command = Command::new("make");
    for &(ref key, ref val) in tooling.env() {
        command.env(key, val);
    }
    command.current_dir(dir)
        .arg(platform)
        .arg(cc)
        .arg(cflags)
        .execute()
}

/// The command to fetch a URL (e.g. with wget) specialized for different
/// OSes.
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos")))]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("wget").arg(url).current_dir(cwd).execute()
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("fetch").arg(url).current_dir(cwd).execute()
}

#[cfg(target_os = "macos")]
fn fetch_in_dir(url: &str, cwd: &Path) -> io::Result<()> {
    Command::new("curl").arg("-O").arg(url).current_dir(cwd).execute()
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() -> io::Result<()> {
    let lua_version = match env::var_os("LUA_VERSION") {
        Some(lua_version) => lua_version,
        None => From::from("5.3.0"),
    };
    let lua_version = lua_version.to_str().unwrap();
    let lua_dir = format!("lua-{}", lua_version);
    let lua_tarball = format!("{}.tar.gz", lua_dir);
    let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let tooling = gcc::Config::new().get_compiler();

    // Ensure the presence of liblua.a
    if !fs::metadata(build_dir.join(&format!("{}/src/liblua.a", lua_dir))).is_ok() {
        try!(fs::create_dir_all(&build_dir));

        // Download lua if it hasn't been already
        if !fs::metadata(build_dir.join(&lua_tarball)).is_ok() {
            match env::var("LUA_LOCAL_SOURCE") {
                Ok(lua_source_path) => {
                    try!(Command::new("cp")
                         .arg(&PathBuf::from(lua_source_path).join(&lua_tarball))
                         .arg(".")
                         .current_dir(&build_dir)
                         .execute());
                }
                Err(_) => {
                    try!(fetch_in_dir(&format!(
                        "http://www.lua.org/ftp/{}", lua_tarball), &build_dir));
                }
            }
            try!(Command::new("tar")
                .arg("xzf")
                .arg(&lua_tarball)
                .current_dir(&build_dir)
                .execute());
        }
        // Compile lua
        try!(build_lua(&tooling, &build_dir.join(&lua_dir)));
    }

    // Ensure the presence of glue.rs
    if !fs::metadata(build_dir.join("glue.rs")).is_ok() {
        // Compile glue.c
        let glue = build_dir.join("glue");
        try!(Command::new("gcc")
            .arg("-I").arg(build_dir.join(&format!("{}/src", lua_dir)))
            .arg("src/glue/glue.c")
            .arg("-o").arg(&glue)
            .execute());
        try!(Command::new(glue)
            .arg(build_dir.join("glue.rs"))
            .execute());
    }

    // Output build information
    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}/{}/src", build_dir.to_str().unwrap(), lua_dir);

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<|endoftext|>


<commit_before>extern crate bindgen;

use bindgen::{Bindings, BindgenOptions};

use std::default::Default;
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

#[cfg(target_os = "freebsd")]
const HEADER_FILE: &'static str = "freebsd.h";
#[cfg(target_os = "linux")]
const HEADER_FILE: &'static str = "linux.h";
#[cfg(target_os = "macos")]
const HEADER_FILE: &'static str = "macos.h";

fn main() {
    let src_dir = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("src");
    let include_file = src_dir.join(HEADER_FILE);
    let mut options: BindgenOptions = Default::default();
    options.clang_args.push(include_file.to_str().unwrap().to_string());
    let bindings = Bindings::generate(&options, None, None).unwrap().to_string();
    File::create(&src_dir.join("raw/ffi.rs")).unwrap().write_all(bindings.as_bytes()).unwrap();
}
<commit_msg>fix(build.rs): write file directly instead of using `bindings.to_string()`<commit_after>extern crate bindgen;

use bindgen::{Bindings, BindgenOptions};

use std::default::Default;
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

#[cfg(target_os = "freebsd")]
const HEADER_FILE: &'static str = "freebsd.h";
#[cfg(target_os = "linux")]
const HEADER_FILE: &'static str = "linux.h";
#[cfg(target_os = "macos")]
const HEADER_FILE: &'static str = "macos.h";

fn main() {
    let src_dir = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("src");
    let include_file = src_dir.join(HEADER_FILE);
    let mut options: BindgenOptions = Default::default();
    options.clang_args.push(include_file.to_str().unwrap().to_string());
    let bindings = Bindings::generate(&options, None, None).unwrap();
    bindings.write(Box::new(File::create(&src_dir.join("raw/ffi.rs")).unwrap())).unwrap()
}
<|endoftext|>


<commit_before>extern crate git2;

use git2::{Repository, StatusOptions};
use std::fs::File;
use std::path::Path;
use std::io::Write;

fn main() {
  let repo = Repo(Repository::open(".").ok());
  let git = repo.commit().map(|x| format!("-g{}", x)).unwrap_or_default();
  let branch = repo.branch().map(|x| format!("-{}", x)).unwrap_or_default();
  let clean = match repo.clean() {
    Some(false) => "-dirty",
    _ => ""
  };
  let version = std::env::var("CARGO_PKG_VERSION").unwrap();
  let version_string = format!("{}{}{}{}", version, git, branch, clean);
  let out_dir = std::env::var("OUT_DIR").unwrap();
  let p = Path::new(&out_dir);
  let mut f = File::create(p.join("version")).unwrap();
  f.write_all(version_string.as_bytes()).unwrap();
}

struct Repo(Option<Repository>);

impl Repo {
  fn commit(&self) -> Option<String> {
    let repo = self.0.as_ref()?;
    let revparse = repo.revparse_single("HEAD").ok()?;
    revparse.short_id().ok()?.as_str().map(ToOwned::to_owned)
  }

  fn clean(&self) -> Option<bool> {
    let repo = self.0.as_ref()?;
    let mut options = StatusOptions::new();
    options
      .include_ignored(false)
      .include_untracked(false);
    let statuses = repo.statuses(Some(&mut options)).ok()?;
    Some(statuses.is_empty())
  }

  fn branch(&self) -> Option<String> {
    let repo = self.0.as_ref()?;
    let head = repo.head().ok()?;
    head.name().and_then(|x| {
      if x.starts_with("refs/heads/") {
        Some(x[11..].to_string())
      } else if x.len() >= 7 {
        Some(x[..7].to_string())
      } else {
        None
      }
    })
  }
}
<commit_msg>refactor: use better methods for branch names and dirty<commit_after>extern crate git2;

use git2::{Repository, DiffOptions};
use std::fs::File;
use std::path::Path;
use std::io::Write;

fn main() {
  let repo = Repo(Repository::open(".").ok());
  let git = repo.commit().map(|x| format!("-g{}", x)).unwrap_or_default();
  let branch = repo.branch().map(|x| format!("-{}", x)).unwrap_or_default();
  let clean = match repo.clean() {
    Some(false) => "-dirty",
    _ => ""
  };
  let version = std::env::var("CARGO_PKG_VERSION").unwrap();
  let version_string = format!(
    "{version}{git}{branch}{clean}",
    version = version,
    git = git,
    branch = branch,
    clean = clean
  );
  let out_dir = std::env::var("OUT_DIR").unwrap();
  let p = Path::new(&out_dir);
  let mut f = File::create(p.join("version")).unwrap();
  f.write_all(version_string.as_bytes()).unwrap();
}

struct Repo(Option<Repository>);

impl Repo {
  fn commit(&self) -> Option<String> {
    let repo = self.0.as_ref()?;
    let revparse = repo.revparse_single("HEAD").ok()?;
    revparse.short_id().ok()?.as_str().map(ToOwned::to_owned)
  }

  fn clean(&self) -> Option<bool> {
    let repo = self.0.as_ref()?;
    let mut options = DiffOptions::new();
    options.ignore_submodules(true);
    let diff = repo.diff_index_to_workdir(None, Some(&mut options)).ok()?;
    Some(diff.stats().ok()?.files_changed() == 0)
  }

  fn branch(&self) -> Option<String> {
    let repo = self.0.as_ref()?;
    let head = repo.head().ok()?;
    head.shorthand().map(ToString::to_string)
  }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use crate::{
    completion::{
        completion_context::CompletionContext,
        completion_item::{Builder, CompletionKind, Completions},
    },
    CompletionItem,
};
use hir::{Ty, TypeCtor};
use ra_syntax::{ast::AstNode, TextRange, TextUnit};
use ra_text_edit::TextEdit;

fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
    let edit = {
        let receiver_range =
            ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
        let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
        TextEdit::replace(delete_range, snippet.to_string())
    };
    CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
        .detail(detail)
        .snippet_edit(edit)
}

fn is_bool_or_unknown(ty: Option<Ty>) -> bool {
    if let Some(ty) = ty {
        match ty {
            Ty::Apply(at) => match at.ctor {
                TypeCtor::Bool => true,
                _ => false,
            },
            Ty::Unknown => true,
            _ => false,
        }
    } else {
        true
    }
}

pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
    if let Some(dot_receiver) = &ctx.dot_receiver {
        let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
            let text = dot_receiver.syntax().text();
            let without_dot = ..text.len() - TextUnit::of_char('.');
            text.slice(without_dot).to_string()
        } else {
            dot_receiver.syntax().text().to_string()
        };
        let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);
        if is_bool_or_unknown(receiver_ty) {
            postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
                .add_to(acc);
            postfix_snippet(
                ctx,
                "while",
                "while expr {}",
                &format!("while {} {{\n$0\n}}", receiver_text),
            )
            .add_to(acc);
        }
        postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
        postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
        postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);
        postfix_snippet(
            ctx,
            "match",
            "match expr {}",
            &format!("match {} {{\n    ${{1:_}} => {{$0\\}},\n}}", receiver_text),
        )
        .add_to(acc);
        postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);
        postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
            .add_to(acc);
    }
}

#[cfg(test)]
mod tests {
    use crate::completion::{do_completion, CompletionItem, CompletionKind};
    use insta::assert_debug_snapshot;

    fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
        do_completion(code, CompletionKind::Postfix)
    }

    #[test]
    fn postfix_completion_works_for_trivial_path_expression() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    let bar = true;
                    bar.<|>
                }
                "#,
            ),
            @r###"[
    CompletionItem {
        label: "box",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "Box::new(bar)",
        detail: "Box::new(expr)",
    },
    CompletionItem {
        label: "dbg",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "dbg!(bar)",
        detail: "dbg!(expr)",
    },
    CompletionItem {
        label: "if",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "if bar {$0}",
        detail: "if expr {}",
    },
    CompletionItem {
        label: "match",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "match bar {\n    ${1:_} => {$0\\},\n}",
        detail: "match expr {}",
    },
    CompletionItem {
        label: "not",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "!bar",
        detail: "!expr",
    },
    CompletionItem {
        label: "ref",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "&bar",
        detail: "&expr",
    },
    CompletionItem {
        label: "refm",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "&mut bar",
        detail: "&mut expr",
    },
    CompletionItem {
        label: "while",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "while bar {\n$0\n}",
        detail: "while expr {}",
    },
]"###
        );
    }

    #[test]
    fn some_postfix_completions_ignored() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    let bar: u8 = 12;
                    bar.<|>
                }
                "#,
            ),
            @r###"[
    CompletionItem {
        label: "box",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "Box::new(bar)",
        detail: "Box::new(expr)",
    },
    CompletionItem {
        label: "dbg",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "dbg!(bar)",
        detail: "dbg!(expr)",
    },
    CompletionItem {
        label: "match",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "match bar {\n    ${1:_} => {$0\\},\n}",
        detail: "match expr {}",
    },
    CompletionItem {
        label: "not",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "!bar",
        detail: "!expr",
    },
    CompletionItem {
        label: "ref",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "&bar",
        detail: "&expr",
    },
    CompletionItem {
        label: "refm",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "&mut bar",
        detail: "&mut expr",
    },
]"###
        );
    }

    #[test]
    fn postfix_completion_works_for_ambiguous_float_literal() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    42.<|>
                }
                "#,
            ),
            @r###"[
    CompletionItem {
        label: "box",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "Box::new(42)",
        detail: "Box::new(expr)",
    },
    CompletionItem {
        label: "dbg",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "dbg!(42)",
        detail: "dbg!(expr)",
    },
    CompletionItem {
        label: "match",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "match 42 {\n    ${1:_} => {$0\\},\n}",
        detail: "match expr {}",
    },
    CompletionItem {
        label: "not",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "!42",
        detail: "!expr",
    },
    CompletionItem {
        label: "ref",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "&42",
        detail: "&expr",
    },
    CompletionItem {
        label: "refm",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "&mut 42",
        detail: "&mut expr",
    },
]"###
        );
    }
}
<commit_msg>Cleanup complete_postfix<commit_after>//! FIXME: write short doc here

use hir::{Ty, TypeCtor};
use ra_syntax::{ast::AstNode, TextRange, TextUnit};
use ra_text_edit::TextEdit;

use crate::{
    completion::{
        completion_context::CompletionContext,
        completion_item::{Builder, CompletionKind, Completions},
    },
    CompletionItem,
};

pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
    let dot_receiver = match &ctx.dot_receiver {
        Some(it) => it,
        None => return,
    };

    let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
        let text = dot_receiver.syntax().text();
        let without_dot = ..text.len() - TextUnit::of_char('.');
        text.slice(without_dot).to_string()
    } else {
        dot_receiver.syntax().text().to_string()
    };

    let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);

    if is_bool_or_unknown(receiver_ty) {
        postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
            .add_to(acc);
        postfix_snippet(
            ctx,
            "while",
            "while expr {}",
            &format!("while {} {{\n$0\n}}", receiver_text),
        )
        .add_to(acc);
    }

    postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);

    postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
    postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);

    postfix_snippet(
        ctx,
        "match",
        "match expr {}",
        &format!("match {} {{\n    ${{1:_}} => {{$0\\}},\n}}", receiver_text),
    )
    .add_to(acc);

    postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);

    postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
        .add_to(acc);
}

fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
    let edit = {
        let receiver_range =
            ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
        let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
        TextEdit::replace(delete_range, snippet.to_string())
    };
    CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
        .detail(detail)
        .snippet_edit(edit)
}

fn is_bool_or_unknown(ty: Option<Ty>) -> bool {
    match &ty {
        Some(Ty::Apply(app)) if app.ctor == TypeCtor::Bool => true,
        Some(Ty::Unknown) | None => true,
        Some(_) => false,
    }
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot;

    use crate::completion::{do_completion, CompletionItem, CompletionKind};

    fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
        do_completion(code, CompletionKind::Postfix)
    }

    #[test]
    fn postfix_completion_works_for_trivial_path_expression() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    let bar = true;
                    bar.<|>
                }
                "#,
            ),
            @r###"[
    CompletionItem {
        label: "box",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "Box::new(bar)",
        detail: "Box::new(expr)",
    },
    CompletionItem {
        label: "dbg",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "dbg!(bar)",
        detail: "dbg!(expr)",
    },
    CompletionItem {
        label: "if",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "if bar {$0}",
        detail: "if expr {}",
    },
    CompletionItem {
        label: "match",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "match bar {\n    ${1:_} => {$0\\},\n}",
        detail: "match expr {}",
    },
    CompletionItem {
        label: "not",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "!bar",
        detail: "!expr",
    },
    CompletionItem {
        label: "ref",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "&bar",
        detail: "&expr",
    },
    CompletionItem {
        label: "refm",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "&mut bar",
        detail: "&mut expr",
    },
    CompletionItem {
        label: "while",
        source_range: [89; 89),
        delete: [85; 89),
        insert: "while bar {\n$0\n}",
        detail: "while expr {}",
    },
]"###
        );
    }

    #[test]
    fn some_postfix_completions_ignored() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    let bar: u8 = 12;
                    bar.<|>
                }
                "#,
            ),
            @r###"[
    CompletionItem {
        label: "box",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "Box::new(bar)",
        detail: "Box::new(expr)",
    },
    CompletionItem {
        label: "dbg",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "dbg!(bar)",
        detail: "dbg!(expr)",
    },
    CompletionItem {
        label: "match",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "match bar {\n    ${1:_} => {$0\\},\n}",
        detail: "match expr {}",
    },
    CompletionItem {
        label: "not",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "!bar",
        detail: "!expr",
    },
    CompletionItem {
        label: "ref",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "&bar",
        detail: "&expr",
    },
    CompletionItem {
        label: "refm",
        source_range: [91; 91),
        delete: [87; 91),
        insert: "&mut bar",
        detail: "&mut expr",
    },
]"###
        );
    }

    #[test]
    fn postfix_completion_works_for_ambiguous_float_literal() {
        assert_debug_snapshot!(
            do_postfix_completion(
                r#"
                fn main() {
                    42.<|>
                }
                "#,
            ),
            @r###"[
    CompletionItem {
        label: "box",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "Box::new(42)",
        detail: "Box::new(expr)",
    },
    CompletionItem {
        label: "dbg",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "dbg!(42)",
        detail: "dbg!(expr)",
    },
    CompletionItem {
        label: "match",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "match 42 {\n    ${1:_} => {$0\\},\n}",
        detail: "match expr {}",
    },
    CompletionItem {
        label: "not",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "!42",
        detail: "!expr",
    },
    CompletionItem {
        label: "ref",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "&42",
        detail: "&expr",
    },
    CompletionItem {
        label: "refm",
        source_range: [52; 52),
        delete: [49; 52),
        insert: "&mut 42",
        detail: "&mut expr",
    },
]"###
        );
    }
}
<|endoftext|>


<commit_before>extern crate std;

pub struct TsPackets<R> {
    reader: R,
}

impl<R: std::io::Read> Iterator for TsPackets<R> {
    type Item = Result<[u8; 188], std::io::Error>;

    fn next(&mut self) -> Option<Result<[u8; 188], std::io::Error>> {
        let mut buf = [0; 188];
        match self.reader.read_exact(&mut buf) {
            Ok(()) => Some(Ok(buf)),
            Err(e) => {
                match e.kind() {
                    std::io::ErrorKind::UnexpectedEof => None,
                    _ => Some(Err(e)),
                }
            }
        }
    }
}

pub fn ts_packets<R>(reader: R) -> TsPackets<R> {
    TsPackets { reader: reader }
}

#[derive(Debug)]
pub struct TsPacket<'a> {
    pub sync_byte: u8,
    pub transport_error_indicator: bool,
    pub payload_unit_start_indicator: bool,
    pub transport_priority: bool,
    pub pid: u16,
    pub transport_scrambling_control: u8,
    pub adaptation_field_control: u8,
    pub continuity_counter: u8,
    pub adaptation_field: Option<AdaptationField<'a>>,
    pub data_bytes: Option<&'a [u8]>,
}

impl<'a> TsPacket<'a> {
    pub fn new(packet: &'a [u8]) -> Self {
        // ISO/IEC 13818-1 2.4.3.2 Table 2-2
        // ISO/IEC 13818-1 2.4.3.3
        let sync_byte = packet[0];
        let transport_error_indicator = (packet[1] & 0b10000000) != 0;
        let payload_unit_start_indicator = (packet[1] & 0b01000000) != 0;
        let transport_priority = (packet[1] & 0b00100000) != 0;
        let pid = ((packet[1] & 0b00011111) as u16) << 8 | (packet[2] as u16);
        let transport_scrambling_control = (packet[3] & 0b11000000) >> 6;
        let adaptation_field_control = (packet[3] & 0b00110000) >> 4;
        let continuity_counter = packet[3] & 0b00001111;

        let mut index = 4;

        let adaptation_field = if adaptation_field_control == 0b10 ||
                                  adaptation_field_control == 0b11 {
            let adaptation_field = AdaptationField::parse(&packet[index..]);
            if let Some(ref af) = adaptation_field {
                index += af.adaptation_field_length as usize + 1;
            } else {
                index += 1
            }
            adaptation_field
        } else {
            None
        };

        let data_bytes = if adaptation_field_control == 0b01 || adaptation_field_control == 0b11 {
            Some(&packet[index..])
        } else {
            None
        };

        TsPacket {
            sync_byte: sync_byte,
            transport_error_indicator: transport_error_indicator,
            payload_unit_start_indicator: payload_unit_start_indicator,
            transport_priority: transport_priority,
            pid: pid,
            transport_scrambling_control: transport_scrambling_control,
            adaptation_field_control: adaptation_field_control,
            continuity_counter: continuity_counter,
            adaptation_field: adaptation_field,
            data_bytes: data_bytes,
        }
    }

    pub fn check_sync_byte(&self) -> bool {
        self.sync_byte == 0x47
    }
}

#[derive(Debug)]
pub struct AdaptationField<'a> {
    pub adaptation_field_length: u8,
    pub discontinuity_indicator: bool,
    pub random_access_indicator: bool,
    pub elementary_stream_priority_indicator: bool,
    pub transport_private_data_flag: bool,
    pub pcr: Option<PCR>,
    pub opcr: Option<OPCR>,
    pub splice_countdown: Option<i8>,
    pub transport_private_data: Option<&'a [u8]>,
    pub adaptation_field_extension: Option<AdaptationFieldExtension<'a>>,
}

impl<'a> AdaptationField<'a> {
    fn parse(packet: &'a [u8]) -> Option<Self> {
        // ISO/IEC 13818-1 2.4.3.4 Table 2-6
        // ISO/IEC 13818-1 2.4.3.5
        let adaptation_field_length = packet[0];
        if adaptation_field_length == 0 {
            None
        } else {
            let discontinuity_indicator = (packet[1] & 0b100000) != 0;
            let random_access_indicator = (packet[1] & 0b010000) != 0;
            let elementary_stream_priority_indicator = (packet[1] & 0b00100000) != 0;
            let pcr_flag = (packet[1] & 0b00010000) != 0;
            let opcr_flag = (packet[1] & 0b00001000) != 0;
            let splicing_point_flag = (packet[1] & 0b00000100) != 0;
            let transport_private_data_flag = (packet[1] & 0b00000010) != 0;
            let adaptation_field_extension_flag = (packet[1] & 0b00000001) != 0;

            let mut index = 2;

            let pcr = if pcr_flag {
                let pcr = PCR::new(&packet[index..]);
                index += PCR::size();
                Some(pcr)
            } else {
                None
            };

            let opcr = if opcr_flag {
                let opcr = OPCR::new(&packet[index..]);
                index += OPCR::size();
                Some(opcr)
            } else {
                None
            };

            let splice_countdown = if splicing_point_flag {
                let splice_countdown = packet[index] as i8;
                index += 1;
                Some(splice_countdown)
            } else {
                None
            };

            let transport_private_data = if transport_private_data_flag {
                let length = packet[index] as usize;
                index += 1;
                let data = &packet[index..(index + length)];
                index += length;
                Some(data)
            } else {
                None
            };

            let adaptation_field_extension = if adaptation_field_extension_flag {
                let extension = AdaptationFieldExtension::new(&packet[index..]);
                index += extension.adaptation_field_extension_length as usize;
                Some(extension)
            } else {
                None
            };

            // Check stuffing_bytes
            for &stuffing_byte in &packet[index..(adaptation_field_length as usize + 1)] {
                if stuffing_byte != 0xff {
                    warn!("Invalid stuffing_byte in adaptation field: {}",
                          stuffing_byte);
                }
            }

            Some(AdaptationField {
                adaptation_field_length: adaptation_field_length,
                discontinuity_indicator: discontinuity_indicator,
                random_access_indicator: random_access_indicator,
                elementary_stream_priority_indicator: elementary_stream_priority_indicator,
                transport_private_data_flag: transport_private_data_flag,
                pcr: pcr,
                opcr: opcr,
                splice_countdown: splice_countdown,
                transport_private_data: transport_private_data,
                adaptation_field_extension: adaptation_field_extension,
            })
        }
    }
}

#[derive(Debug)]
pub struct PCR {
    pub program_clock_reference_base: u64,
    pub reserved: u8,
    pub program_clock_reference_extension: u16,
}

impl PCR {
    fn new(packet: &[u8]) -> Self {
        PCR {
            program_clock_reference_base: ((packet[0] as u64) << 32) | ((packet[1] as u64) << 24) |
                                          ((packet[2] as u64) << 16) |
                                          (packet[3] as u64) << 8 |
                                          (packet[4] & 0b10000000) as u64,
            reserved: packet[4] & 0b01111110,
            program_clock_reference_extension: ((packet[4] & 0b00000001) as u16) << 8 |
                                               packet[5] as u16,
        }
    }

    fn size() -> usize {
        6
    }
}

#[derive(Debug)]
pub struct OPCR {
    pub original_program_clock_reference_base: u64,
    pub reserved: u8,
    pub original_program_clock_reference_extension: u16,
}

impl OPCR {
    fn new(packet: &[u8]) -> Self {
        OPCR {
            original_program_clock_reference_base: ((packet[0] as u64) << 32) |
                                                   ((packet[1] as u64) << 24) |
                                                   ((packet[2] as u64) << 16) |
                                                   (packet[3] as u64) << 8 |
                                                   (packet[4] & 0b10000000) as u64,
            reserved: packet[4] & 0b01111110,
            original_program_clock_reference_extension: ((packet[4] & 0b00000001) as u16) << 8 |
                                                        packet[5] as u16,
        }
    }

    fn size() -> usize {
        6
    }
}

#[derive(Debug)]
pub struct AdaptationFieldExtension<'a> {
    pub adaptation_field_extension_length: u8,
    pub reserved: u8,
    pub ltw: Option<LegalTimeWindow>,
    pub piecewise_rate: Option<u32>,
    pub seamless_splice: Option<SeamlessSplice>,
    pub trailing_reserved: &'a [u8],
}

impl<'a> AdaptationFieldExtension<'a> {
    fn new(packet: &'a [u8]) -> Self {
        let adaptation_field_extension_length = packet[0];
        let ltw_flag = (packet[1] & 0b10000000) != 0;
        let piecewise_rate_flag = (packet[1] & 0b01000000) != 0;
        let seamless_splice_flag = (packet[1] & 0b00100000) != 0;
        let reserved = packet[1] & 0b00011111;

        let mut index = 2;

        let ltw = if ltw_flag {
            let ltw = LegalTimeWindow::new(&packet[index..]);
            index += LegalTimeWindow::size();
            Some(ltw)
        } else {
            None
        };

        let piecewise_rate = if piecewise_rate_flag {
            let rate = ((packet[index] & 0b00111111) as u32) << 16 |
                       ((packet[index + 1] as u32) << 16) |
                       (packet[index + 1] as u32);
            index += 3;
            Some(rate)
        } else {
            None
        };

        let seamless_splice = if seamless_splice_flag {
            let splice = SeamlessSplice::new(&packet[index..]);
            index += SeamlessSplice::size();
            Some(splice)
        } else {
            None
        };

        let trailing_reserved = &packet[index..];

        AdaptationFieldExtension {
            adaptation_field_extension_length: adaptation_field_extension_length,
            reserved: reserved,
            ltw: ltw,
            piecewise_rate: piecewise_rate,
            seamless_splice: seamless_splice,
            trailing_reserved: trailing_reserved,
        }
    }
}

#[derive(Debug)]
pub struct LegalTimeWindow {
    pub ltw_valid_flag: bool,
    pub ltw_offset: u16,
}

impl LegalTimeWindow {
    fn new(packet: &[u8]) -> Self {
        LegalTimeWindow {
            ltw_valid_flag: (packet[0] & 0b10000000) != 0,
            ltw_offset: ((packet[0] & 0b01111111) as u16) << 8 | (packet[1] as u16),
        }
    }

    fn size() -> usize {
        2
    }
}

#[derive(Debug)]
pub struct SeamlessSplice {
    pub splice_type: u8,
    pub dts_next_au: u64,
}

impl SeamlessSplice {
    fn new(packet: &[u8]) -> Self {
        SeamlessSplice {
            splice_type: packet[0] & 0b11110000,
            dts_next_au: ((((packet[0] & 0b00001110) >> 1) as u64) << 30 |
                          ((packet[1] >> 1) as u64) << 15 |
                          ((packet[2] >> 1) as u64)),
        }
    }

    fn size() -> usize {
        5
    }
}
<commit_msg>Reuse buffer<commit_after>extern crate std;

pub struct TsPackets<R> {
    reader: R,
    buf: [u8; 188],
}

impl<R: std::io::Read> Iterator for TsPackets<R> {
    type Item = Result<[u8; 188], std::io::Error>;

    fn next(&mut self) -> Option<Result<[u8; 188], std::io::Error>> {
        match self.reader.read_exact(&mut self.buf) {
            Ok(()) => Some(Ok(self.buf)),
            Err(e) => {
                match e.kind() {
                    std::io::ErrorKind::UnexpectedEof => None,
                    _ => Some(Err(e)),
                }
            }
        }
    }
}

pub fn ts_packets<R>(reader: R) -> TsPackets<R> {
    TsPackets {
        reader: reader,
        buf: [0; 188],
    }
}

#[derive(Debug)]
pub struct TsPacket<'a> {
    pub sync_byte: u8,
    pub transport_error_indicator: bool,
    pub payload_unit_start_indicator: bool,
    pub transport_priority: bool,
    pub pid: u16,
    pub transport_scrambling_control: u8,
    pub adaptation_field_control: u8,
    pub continuity_counter: u8,
    pub adaptation_field: Option<AdaptationField<'a>>,
    pub data_bytes: Option<&'a [u8]>,
}

impl<'a> TsPacket<'a> {
    pub fn new(packet: &'a [u8]) -> Self {
        // ISO/IEC 13818-1 2.4.3.2 Table 2-2
        // ISO/IEC 13818-1 2.4.3.3
        let sync_byte = packet[0];
        let transport_error_indicator = (packet[1] & 0b10000000) != 0;
        let payload_unit_start_indicator = (packet[1] & 0b01000000) != 0;
        let transport_priority = (packet[1] & 0b00100000) != 0;
        let pid = ((packet[1] & 0b00011111) as u16) << 8 | (packet[2] as u16);
        let transport_scrambling_control = (packet[3] & 0b11000000) >> 6;
        let adaptation_field_control = (packet[3] & 0b00110000) >> 4;
        let continuity_counter = packet[3] & 0b00001111;

        let mut index = 4;

        let adaptation_field = if adaptation_field_control == 0b10 ||
                                  adaptation_field_control == 0b11 {
            let adaptation_field = AdaptationField::parse(&packet[index..]);
            if let Some(ref af) = adaptation_field {
                index += af.adaptation_field_length as usize + 1;
            } else {
                index += 1
            }
            adaptation_field
        } else {
            None
        };

        let data_bytes = if adaptation_field_control == 0b01 || adaptation_field_control == 0b11 {
            Some(&packet[index..])
        } else {
            None
        };

        TsPacket {
            sync_byte: sync_byte,
            transport_error_indicator: transport_error_indicator,
            payload_unit_start_indicator: payload_unit_start_indicator,
            transport_priority: transport_priority,
            pid: pid,
            transport_scrambling_control: transport_scrambling_control,
            adaptation_field_control: adaptation_field_control,
            continuity_counter: continuity_counter,
            adaptation_field: adaptation_field,
            data_bytes: data_bytes,
        }
    }

    pub fn check_sync_byte(&self) -> bool {
        self.sync_byte == 0x47
    }
}

#[derive(Debug)]
pub struct AdaptationField<'a> {
    pub adaptation_field_length: u8,
    pub discontinuity_indicator: bool,
    pub random_access_indicator: bool,
    pub elementary_stream_priority_indicator: bool,
    pub transport_private_data_flag: bool,
    pub pcr: Option<PCR>,
    pub opcr: Option<OPCR>,
    pub splice_countdown: Option<i8>,
    pub transport_private_data: Option<&'a [u8]>,
    pub adaptation_field_extension: Option<AdaptationFieldExtension<'a>>,
}

impl<'a> AdaptationField<'a> {
    fn parse(packet: &'a [u8]) -> Option<Self> {
        // ISO/IEC 13818-1 2.4.3.4 Table 2-6
        // ISO/IEC 13818-1 2.4.3.5
        let adaptation_field_length = packet[0];
        if adaptation_field_length == 0 {
            None
        } else {
            let discontinuity_indicator = (packet[1] & 0b100000) != 0;
            let random_access_indicator = (packet[1] & 0b010000) != 0;
            let elementary_stream_priority_indicator = (packet[1] & 0b00100000) != 0;
            let pcr_flag = (packet[1] & 0b00010000) != 0;
            let opcr_flag = (packet[1] & 0b00001000) != 0;
            let splicing_point_flag = (packet[1] & 0b00000100) != 0;
            let transport_private_data_flag = (packet[1] & 0b00000010) != 0;
            let adaptation_field_extension_flag = (packet[1] & 0b00000001) != 0;

            let mut index = 2;

            let pcr = if pcr_flag {
                let pcr = PCR::new(&packet[index..]);
                index += PCR::size();
                Some(pcr)
            } else {
                None
            };

            let opcr = if opcr_flag {
                let opcr = OPCR::new(&packet[index..]);
                index += OPCR::size();
                Some(opcr)
            } else {
                None
            };

            let splice_countdown = if splicing_point_flag {
                let splice_countdown = packet[index] as i8;
                index += 1;
                Some(splice_countdown)
            } else {
                None
            };

            let transport_private_data = if transport_private_data_flag {
                let length = packet[index] as usize;
                index += 1;
                let data = &packet[index..(index + length)];
                index += length;
                Some(data)
            } else {
                None
            };

            let adaptation_field_extension = if adaptation_field_extension_flag {
                let extension = AdaptationFieldExtension::new(&packet[index..]);
                index += extension.adaptation_field_extension_length as usize;
                Some(extension)
            } else {
                None
            };

            // Check stuffing_bytes
            for &stuffing_byte in &packet[index..(adaptation_field_length as usize + 1)] {
                if stuffing_byte != 0xff {
                    warn!("Invalid stuffing_byte in adaptation field: {}",
                          stuffing_byte);
                }
            }

            Some(AdaptationField {
                adaptation_field_length: adaptation_field_length,
                discontinuity_indicator: discontinuity_indicator,
                random_access_indicator: random_access_indicator,
                elementary_stream_priority_indicator: elementary_stream_priority_indicator,
                transport_private_data_flag: transport_private_data_flag,
                pcr: pcr,
                opcr: opcr,
                splice_countdown: splice_countdown,
                transport_private_data: transport_private_data,
                adaptation_field_extension: adaptation_field_extension,
            })
        }
    }
}

#[derive(Debug)]
pub struct PCR {
    pub program_clock_reference_base: u64,
    pub reserved: u8,
    pub program_clock_reference_extension: u16,
}

impl PCR {
    fn new(packet: &[u8]) -> Self {
        PCR {
            program_clock_reference_base: ((packet[0] as u64) << 32) | ((packet[1] as u64) << 24) |
                                          ((packet[2] as u64) << 16) |
                                          (packet[3] as u64) << 8 |
                                          (packet[4] & 0b10000000) as u64,
            reserved: packet[4] & 0b01111110,
            program_clock_reference_extension: ((packet[4] & 0b00000001) as u16) << 8 |
                                               packet[5] as u16,
        }
    }

    fn size() -> usize {
        6
    }
}

#[derive(Debug)]
pub struct OPCR {
    pub original_program_clock_reference_base: u64,
    pub reserved: u8,
    pub original_program_clock_reference_extension: u16,
}

impl OPCR {
    fn new(packet: &[u8]) -> Self {
        OPCR {
            original_program_clock_reference_base: ((packet[0] as u64) << 32) |
                                                   ((packet[1] as u64) << 24) |
                                                   ((packet[2] as u64) << 16) |
                                                   (packet[3] as u64) << 8 |
                                                   (packet[4] & 0b10000000) as u64,
            reserved: packet[4] & 0b01111110,
            original_program_clock_reference_extension: ((packet[4] & 0b00000001) as u16) << 8 |
                                                        packet[5] as u16,
        }
    }

    fn size() -> usize {
        6
    }
}

#[derive(Debug)]
pub struct AdaptationFieldExtension<'a> {
    pub adaptation_field_extension_length: u8,
    pub reserved: u8,
    pub ltw: Option<LegalTimeWindow>,
    pub piecewise_rate: Option<u32>,
    pub seamless_splice: Option<SeamlessSplice>,
    pub trailing_reserved: &'a [u8],
}

impl<'a> AdaptationFieldExtension<'a> {
    fn new(packet: &'a [u8]) -> Self {
        let adaptation_field_extension_length = packet[0];
        let ltw_flag = (packet[1] & 0b10000000) != 0;
        let piecewise_rate_flag = (packet[1] & 0b01000000) != 0;
        let seamless_splice_flag = (packet[1] & 0b00100000) != 0;
        let reserved = packet[1] & 0b00011111;

        let mut index = 2;

        let ltw = if ltw_flag {
            let ltw = LegalTimeWindow::new(&packet[index..]);
            index += LegalTimeWindow::size();
            Some(ltw)
        } else {
            None
        };

        let piecewise_rate = if piecewise_rate_flag {
            let rate = ((packet[index] & 0b00111111) as u32) << 16 |
                       ((packet[index + 1] as u32) << 16) |
                       (packet[index + 1] as u32);
            index += 3;
            Some(rate)
        } else {
            None
        };

        let seamless_splice = if seamless_splice_flag {
            let splice = SeamlessSplice::new(&packet[index..]);
            index += SeamlessSplice::size();
            Some(splice)
        } else {
            None
        };

        let trailing_reserved = &packet[index..];

        AdaptationFieldExtension {
            adaptation_field_extension_length: adaptation_field_extension_length,
            reserved: reserved,
            ltw: ltw,
            piecewise_rate: piecewise_rate,
            seamless_splice: seamless_splice,
            trailing_reserved: trailing_reserved,
        }
    }
}

#[derive(Debug)]
pub struct LegalTimeWindow {
    pub ltw_valid_flag: bool,
    pub ltw_offset: u16,
}

impl LegalTimeWindow {
    fn new(packet: &[u8]) -> Self {
        LegalTimeWindow {
            ltw_valid_flag: (packet[0] & 0b10000000) != 0,
            ltw_offset: ((packet[0] & 0b01111111) as u16) << 8 | (packet[1] as u16),
        }
    }

    fn size() -> usize {
        2
    }
}

#[derive(Debug)]
pub struct SeamlessSplice {
    pub splice_type: u8,
    pub dts_next_au: u64,
}

impl SeamlessSplice {
    fn new(packet: &[u8]) -> Self {
        SeamlessSplice {
            splice_type: packet[0] & 0b11110000,
            dts_next_au: ((((packet[0] & 0b00001110) >> 1) as u64) << 30 |
                          ((packet[1] >> 1) as u64) << 15 |
                          ((packet[2] >> 1) as u64)),
        }
    }

    fn size() -> usize {
        5
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize

mod log

#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, RustcDecodable)]
enum DeathCallback {
    Player,
    Monster,
}

impl DeathCallback {
    fn callback(self, object: &mut Object, messages: &mut log::Messages) {
        use DeathCallback::*;
        let callback: fn(&mut Object, &mut log::Messages) = match self {
            Player => player_death,
            Monster => monster_death,
        };
        callback(object, messages);
    }
}

#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, RustcDecodable)]
struct Fighter {
    max_hp: i32,
    hp: i32,
    defense: i32,
    power: i32,
    on_death: DeathCallback,
}
<commit_msg>Remove superfluous actor.rs (real one is in object/)<commit_after><|endoftext|>