<commit_before>pub struct GPU {
    mode: Mode,
    line: u32,
    cycles: u32,
    vram: VideoMemory
}

/// Video memory and related data/registers
/// The main VRAM (`data`) is used for the following values:
///     8000-87FF	Tile set #1: tiles 0-127
///     8800-8FFF	Tile set #1: tiles 128-255 Tile set #0: tiles -1 to -128
///     9000-97FF	Tile set #0: tiles 0-127
///     9800-9BFF	Tile map #0
///     9C00-9FFF	Tile map #1
///
/// There are 2 tile sets of 256 tiles, but 128 tiles are shared between them, so the
/// total amounts to 384 tiles.
/// The tile maps hold indexes to a corresponding tile in the tilesets.
/// Pixel data is 2 bits and is split between two adjacent bytes, low bit first:
///
///     1 0 0 0 1 1 0 1 -- 0x8000
///     0 1 1 0 1 0 1 1 -- 0x8001
///
///     1 2 2 0 3 1 2 3 -- pixel value (0 to 3)
///
/// The rest of the memory (`oam`) is used for sprite data and addressed separately
/// from FE00 to FE9F.
pub struct VideoMemory {
    data: [u8; 8192],
    oam: [u8; 160],
}

///  Mode 0 (HBlank): The LCD controller is in the H-Blank period and
///          the CPU can access both the display RAM (8000h-9FFFh)
///          and OAM (FE00h-FE9Fh)
///
///  Mode 1 (VBlank): The LCD controller is in the V-Blank period (or the
///          display is disabled) and the CPU can access both the
///          display RAM (8000h-9FFFh) and OAM (FE00h-FE9Fh)
///
///  Mode 2 (OAMRead): The LCD controller is reading from OAM memory.
///          The CPU <cannot> access OAM memory (FE00h-FE9Fh)
///          during this period.
///
///  Mode 3 (VRAMRead): The LCD controller is reading from both OAM and VRAM,
///          The CPU <cannot> access OAM and VRAM during this period.
///          CGB Mode: Cannot access Palette Data (FF69,FF6B) either.
enum Mode {
    HBlank,
    VBlank,
    OAMRead,
    VRAMRead,
}

/// An enum representing the possible color values for a pixel in the original GB.
/// While the GB is said to be monochromatic, it can actually display 4 different shades.
#[derive(Copy, Clone)]
enum GBColor {
    // white, rgb #FFFFFF
    Off = 0,
    // light grey, rgb #C0C0C0
    On33 = 1,
    // dark grey, rgb #606060
    On66 = 2,
    // black, rgb #000000
    On = 3,
}

/// A tile is an 8x8 square of pixels, each one stored in one of the VRAM's tilesets.
struct Tile {
    pixels: [GBColor; 64],
}

impl Tile {
    pub fn new() -> Tile {
        Tile { pixels: [GBColor::Off; 64] }
    }
}

impl GPU {
    pub fn new() -> GPU {
        GPU {
            mode: Mode::HBlank,
            line: 0,
            cycles: 0,
            vram: VideoMemory {
                data: [0; 8192],
                oam: [0; 160],
            },
        }
    }

    pub fn write_vram(&mut self, addr: usize, value: u8) {
        self.vram.data[addr] = value;
    }

    pub fn read_vram(&self, addr: usize) -> u8 {
        self.vram.data[addr]
    }

    pub fn write_oam(&mut self, addr: usize, value: u8) {
        self.vram.oam[addr] = value;
    }

    pub fn read_oam(&self, addr: usize) -> u8 {
        self.vram.oam[addr]
    }

    /// Emulates the GPU.
    /// This function should be called after an instruction is executed by the CPU,
    /// `delta` is the number of cycles passed from the last instruction.
    pub fn emulate(&mut self, delta: u32) {

        self.cycles += delta;

        match self.mode {
            Mode::OAMRead => {
                if self.cycles >= 80 {
                    self.cycles = 0;
                    self.mode = Mode::VRAMRead;
                }
            }
            Mode::VRAMRead => {
                if self.cycles >= 172 {
                    self.cycles = 0;
                    self.mode = Mode::HBlank;

                    // TODO: scanline is done, write it to framebuffer
                }
            }
            Mode::HBlank => {
                if self.cycles >= 204 {
                    self.cycles = 0;
                    self.line += 1;

                    self.mode = if self.line == 143 {
                        Mode::VBlank
                    } else {
                        Mode::OAMRead
                    };
                }
            }
            Mode::VBlank => {
                if self.cycles >= 456 {
                    self.mode = Mode::HBlank;
                    self.line += 1;

                    if self.line > 153 {
                        self.mode = Mode::OAMRead;
                        self.line = 0;
                    }
                }
            }
        }
    }
}
<commit_msg>Added tile retrieval from VRAM<commit_after>pub struct GPU {
    mode: Mode,
    line: u32,
    cycles: u32,
    vram: VideoMemory
}

/// Video memory and related data/registers
/// The main VRAM (`data`) is used for the following values:
///     8000-87FF	Tile set #1: tiles 0-127
///     8800-8FFF	Tile set #1: tiles 128-255 Tile set #0: tiles -1 to -128
///     9000-97FF	Tile set #0: tiles 0-127
///     9800-9BFF	Tile map #0
///     9C00-9FFF	Tile map #1
///
/// There are 2 tile sets of 256 tiles, but 128 tiles are shared between them, so the
/// total amounts to 384 tiles.
/// The tile maps hold indexes to a corresponding tile in the tilesets.
/// Pixel data is 2 bits and is split between two adjacent bytes, low bit first:
///
///     1 0 0 0 1 1 0 1 -- 0x8000
///     0 1 1 0 1 0 1 1 -- 0x8001
///
///     1 2 2 0 3 1 2 3 -- pixel value (0 to 3)
///
/// The rest of the memory (`oam`) is used for sprite data and addressed separately
/// from FE00 to FE9F.
pub struct VideoMemory {
    data: [u8; 8192],
    oam: [u8; 160],
}

/// An enum used to discriminate tilesets (and maps)
enum TileSelector {
    Set0, Set1
}

///  Mode 0 (HBlank): The LCD controller is in the H-Blank period and
///          the CPU can access both the display RAM (8000h-9FFFh)
///          and OAM (FE00h-FE9Fh)
///
///  Mode 1 (VBlank): The LCD controller is in the V-Blank period (or the
///          display is disabled) and the CPU can access both the
///          display RAM (8000h-9FFFh) and OAM (FE00h-FE9Fh)
///
///  Mode 2 (OAMRead): The LCD controller is reading from OAM memory.
///          The CPU <cannot> access OAM memory (FE00h-FE9Fh)
///          during this period.
///
///  Mode 3 (VRAMRead): The LCD controller is reading from both OAM and VRAM,
///          The CPU <cannot> access OAM and VRAM during this period.
///          CGB Mode: Cannot access Palette Data (FF69,FF6B) either.
enum Mode {
    HBlank,
    VBlank,
    OAMRead,
    VRAMRead,
}

/// An enum representing the possible color values for a pixel in the original GB.
/// While the GB is said to be monochromatic, it can actually display 4 different shades.
#[derive(Copy, Clone, PartialEq, Debug)]
enum GBColor {
    // white, rgb #FFFFFF
    Off = 0,
    // light grey, rgb #C0C0C0
    On33 = 1,
    // dark grey, rgb #606060
    On66 = 2,
    // black, rgb #000000
    On = 3,
}

impl GBColor {
    pub fn from_u8(number: u8) -> GBColor {
        match number {
            0 => GBColor::Off,
            1 => GBColor::On33,
            2 => GBColor::On66,
            3 => GBColor::On,
            _ => panic!("Invalid color value {}", number),
        }
    }
}

/// A tile is an 8x8 square of pixels, each one stored in one of the VRAM's tilesets.
struct Tile {
    pixels: [GBColor; 64],
}

impl Tile {
    pub fn new() -> Tile {
        Tile { pixels: [GBColor::Off; 64] }
    }
}

impl GPU {
    pub fn new() -> GPU {
        GPU {
            mode: Mode::HBlank,
            line: 0,
            cycles: 0,
            vram: VideoMemory {
                data: [0; 8192],
                oam: [0; 160],
            },
        }
    }

    /// Retrieves Tile information from the VRAM.
    /// A tile is held in 16 bytes in the VRAM, enough information for 64 pixels (8x8 matrix, 2 bits per pixel).
    /// When selecting tiles from Set #1, the index 0 represents tile -128 (equal to tile 128 from Set #0)
    fn get_tile(&self, set: TileSelector, tile_index: usize) -> Tile {
        // Set1 starts after 128 tiles, or after 16 * 128 = 2048 (0x800) bytes
        let offset = if let TileSelector::Set1 = set { 0x800 } else { 0 };
        let start_addr = (offset + tile_index) * 0x10;
        let end_addr = start_addr + 0x10;

        let mut addr = start_addr;
        let mut pixels = [GBColor::Off; 64];

        while addr < end_addr {
            let (low, high) = (self.vram.data[addr], self.vram.data[addr + 1]);

            for i in 0..8 {
                let low_bit = (low >> i) & 0x01;
                let high_bit = (high >> i) & 0x01;

                let pixel_value = GBColor::from_u8(low_bit + high_bit * 2);
                // px 0..7, then 8..15
                let pixel_addr = (addr / 2) * 8 + i;
                pixels[pixel_addr as usize] = pixel_value;
            }

            addr += 2;
        }

        Tile { pixels: pixels }
    }

    pub fn write_vram(&mut self, addr: usize, value: u8) {
        self.vram.data[addr] = value;
    }

    pub fn read_vram(&self, addr: usize) -> u8 {
        self.vram.data[addr]
    }

    pub fn write_oam(&mut self, addr: usize, value: u8) {
        self.vram.oam[addr] = value;
    }

    pub fn read_oam(&self, addr: usize) -> u8 {
        self.vram.oam[addr]
    }

    /// Emulates the GPU.
    /// This function should be called after an instruction is executed by the CPU,
    /// `delta` is the number of cycles passed from the last instruction.
    pub fn emulate(&mut self, delta: u32) {

        self.cycles += delta;

        match self.mode {
            Mode::OAMRead => {
                if self.cycles >= 80 {
                    self.cycles = 0;
                    self.mode = Mode::VRAMRead;
                }
            }
            Mode::VRAMRead => {
                if self.cycles >= 172 {
                    self.cycles = 0;
                    self.mode = Mode::HBlank;

                    // TODO: scanline is done, write it to framebuffer
                }
            }
            Mode::HBlank => {
                if self.cycles >= 204 {
                    self.cycles = 0;
                    self.line += 1;

                    self.mode = if self.line == 143 {
                        Mode::VBlank
                    } else {
                        Mode::OAMRead
                    };
                }
            }
            Mode::VBlank => {
                if self.cycles >= 456 {
                    self.mode = Mode::HBlank;
                    self.line += 1;

                    if self.line > 153 {
                        self.mode = Mode::OAMRead;
                        self.line = 0;
                    }
                }
            }
        }
    }
}

#[test]
fn get_tile_test() {
    let mut gpu = GPU::new();
    // write first line of tile #0 in set #0
    // pixels should be: 0 1 2 3 3 2 1 0
    gpu.write_vram(0, 0b0101_1010u8);
    gpu.write_vram(1, 0b0011_1100u8);

    let tile = gpu.get_tile(TileSelector::Set0, 0);

    assert_eq!(tile.pixels[0], GBColor::Off);
    assert_eq!(tile.pixels[1], GBColor::On33);
    assert_eq!(tile.pixels[2], GBColor::On66);
    assert_eq!(tile.pixels[3], GBColor::On);

    assert_eq!(tile.pixels[4], GBColor::On);
    assert_eq!(tile.pixels[5], GBColor::On66);
    assert_eq!(tile.pixels[6], GBColor::On33);
    assert_eq!(tile.pixels[7], GBColor::Off);

    // rest of the pixels are off
    for i in 8..64 {
        assert_eq!(tile.pixels[i], GBColor::Off);
    }
}<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::mem::size_of;

#[derive(Clone, Debug, Ord, Eq, PartialEq, Serialize, Deserialize)]
pub(crate) enum Bound {
    Inclusive(Vec<u8>),
    Exclusive(Vec<u8>),
    Inf,
}

impl Bound {
    pub(crate) fn inner(&self) -> &[u8] {
        match *self {
            Bound::Inclusive(ref v) | Bound::Exclusive(ref v) => &*v,
            Bound::Inf => panic!("inner() called on Bound::Inf"),
        }
    }

    #[inline]
    pub(crate) fn size_in_bytes(&self) -> usize {
        let self_sz = size_of::<Bound>();

        let inner_sz = match self {
            Bound::Inclusive(ref v) | Bound::Exclusive(ref v) => {
                v.len() + size_of::<Vec<u8>>()
            }
            Bound::Inf => 0,
        };

        self_sz + inner_sz
    }
}

impl PartialOrd for Bound {
    fn partial_cmp(&self, other: &Bound) -> Option<Ordering> {
        use self::Bound::*;
        match *self {
            Inclusive(ref lhs) => match *other {
                Inf => Some(Ordering::Less),
                Inclusive(ref rhs) => Some(lhs.cmp(rhs)),
                Exclusive(ref rhs) => {
                    if lhs < rhs {
                        Some(Ordering::Less)
                    } else {
                        Some(Ordering::Greater)
                    }
                }
            },
            Exclusive(ref lhs) => match *other {
                Inf => Some(Ordering::Less),
                Inclusive(ref rhs) => {
                    if lhs <= rhs {
                        Some(Ordering::Less)
                    } else {
                        Some(Ordering::Greater)
                    }
                }
                Exclusive(ref rhs) => Some(lhs.cmp(rhs)),
            },
            Inf => match *other {
                Inf => Some(Ordering::Equal),
                _ => Some(Ordering::Greater),
            },
        }
    }
}

#[test]
fn test_bounds() {
    use self::Bound::*;
    assert_eq!(Inf, Inf);
    assert_eq!(Exclusive(vec![]), Exclusive(vec![]));
    assert_eq!(Inclusive(vec![]), Inclusive(vec![]));
    assert_eq!(Inclusive(b"hi".to_vec()), Inclusive(b"hi".to_vec()));
    assert_eq!(Exclusive(b"hi".to_vec()), Exclusive(b"hi".to_vec()));
    assert!(Inclusive(b"hi".to_vec()) > Exclusive(b"hi".to_vec()));
    assert!(Inclusive(vec![]) < Inf);
    assert!(Exclusive(vec![]) < Inf);
    assert!(Inf > Inclusive(vec![0, 0, 0, 0, 0, 0, 136, 184]));
}
<commit_msg>Improve bounds tests<commit_after>use std::cmp::Ordering;
use std::mem::size_of;

#[derive(Clone, Debug, Ord, Eq, PartialEq, Serialize, Deserialize)]
pub(crate) enum Bound {
    Inclusive(Vec<u8>),
    Exclusive(Vec<u8>),
    Inf,
}

impl Bound {
    pub(crate) fn inner(&self) -> &[u8] {
        match *self {
            Bound::Inclusive(ref v) | Bound::Exclusive(ref v) => &*v,
            Bound::Inf => panic!("inner() called on Bound::Inf"),
        }
    }

    #[inline]
    pub(crate) fn size_in_bytes(&self) -> usize {
        let self_sz = size_of::<Bound>();

        let inner_sz = match self {
            Bound::Inclusive(ref v) | Bound::Exclusive(ref v) => {
                v.len() + size_of::<Vec<u8>>()
            }
            Bound::Inf => 0,
        };

        self_sz + inner_sz
    }
}

impl PartialOrd for Bound {
    fn partial_cmp(&self, other: &Bound) -> Option<Ordering> {
        use self::Bound::*;
        match *self {
            Inclusive(ref lhs) => match *other {
                Inf => Some(Ordering::Less),
                Inclusive(ref rhs) => Some(lhs.cmp(rhs)),
                Exclusive(ref rhs) => {
                    if lhs < rhs {
                        Some(Ordering::Less)
                    } else {
                        Some(Ordering::Greater)
                    }
                }
            },
            Exclusive(ref lhs) => match *other {
                Inf => Some(Ordering::Less),
                Inclusive(ref rhs) => {
                    if lhs <= rhs {
                        Some(Ordering::Less)
                    } else {
                        Some(Ordering::Greater)
                    }
                }
                Exclusive(ref rhs) => Some(lhs.cmp(rhs)),
            },
            Inf => match *other {
                Inf => Some(Ordering::Equal),
                _ => Some(Ordering::Greater),
            },
        }
    }
}

#[test]
fn test_bounds() {
    use self::Bound::*;
    assert_eq!(Inf, Inf);
    assert_eq!(Exclusive(vec![]), Exclusive(vec![]));
    assert_eq!(Inclusive(vec![]), Inclusive(vec![]));
    assert_eq!(Inclusive(b"hi".to_vec()), Inclusive(b"hi".to_vec()));
    assert_eq!(Exclusive(b"hi".to_vec()), Exclusive(b"hi".to_vec()));
    assert!(Inclusive(b"hi".to_vec()) > Exclusive(b"hi".to_vec()));
    assert!(Exclusive(b"hi".to_vec()) < Inclusive(b"hi".to_vec()));
    assert!(Inclusive(b"a".to_vec()) < Inclusive(b"z".to_vec()));
    assert!(Exclusive(b"a".to_vec()) < Exclusive(b"z".to_vec()));
    assert!(Inclusive(b"z".to_vec()) > Inclusive(b"a".to_vec()));
    assert!(Exclusive(b"z".to_vec()) > Exclusive(b"a".to_vec()));
    assert!(Inclusive(vec![]) < Inf);
    assert!(Exclusive(vec![]) < Inf);
    assert!(Inf > Inclusive(vec![0, 0, 0, 0, 0, 0, 136, 184]));
}
<|endoftext|>


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

//! Implements parallel traversals over the DOM and flow trees.
//!
//! This code is highly unsafe. Keep this file small and easy to audit.

use css::matching::MatchMethods;
use layout::context::LayoutContext;
use layout::flow::{Flow, FlowLeafSet, PostorderFlowTraversal};
use layout::flow;
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal};
use layout::util::{LayoutDataAccess, OpaqueNode};
use layout::wrapper::{layout_node_to_unsafe_layout_node, LayoutNode, UnsafeLayoutNode};

use extra::arc::MutexArc;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
use std::cast;
use std::ptr;
use std::sync::atomics::{AtomicInt, Relaxed, SeqCst};
use style::{Stylist, TNode};

pub enum TraversalKind {
    BubbleWidthsTraversalKind,
    AssignHeightsAndStoreOverflowTraversalKind,
}

pub type UnsafeFlow = (uint, uint);

fn null_unsafe_flow() -> UnsafeFlow {
    (0, 0)
}

pub fn owned_flow_to_unsafe_flow(flow: *~Flow) -> UnsafeFlow {
    unsafe {
        cast::transmute_copy(&*flow)
    }
}

pub fn mut_owned_flow_to_unsafe_flow(flow: *mut ~Flow) -> UnsafeFlow {
    unsafe {
        cast::transmute_copy(&*flow)
    }
}

/// Information that we need stored in each DOM node.
pub struct DomParallelInfo {
    /// The number of children that still need work done.
    children_count: AtomicInt,
}

impl DomParallelInfo {
    pub fn new() -> DomParallelInfo {
        DomParallelInfo {
            children_count: AtomicInt::new(0),
        }
    }
}

/// Information that we need stored in each flow.
pub struct FlowParallelInfo {
    /// The number of children that still need work done.
    children_count: AtomicInt,
    /// The address of the parent flow.
    parent: UnsafeFlow,
}

impl FlowParallelInfo {
    pub fn new() -> FlowParallelInfo {
        FlowParallelInfo {
            children_count: AtomicInt::new(0),
            parent: null_unsafe_flow(),
        }
    }
}

/// A parallel bottom-up flow traversal.
trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
    fn run_parallel(&mut self, mut unsafe_flow: UnsafeFlow) {
        loop {
            unsafe {
                // Get a real flow.
                let flow: &mut ~Flow = cast::transmute(&unsafe_flow);

                // Perform the appropriate traversal.
                if self.should_process(*flow) {
                    self.process(*flow);
                }

                let base = flow::mut_base(*flow);

                // Reset the count of children for the next layout traversal.
                base.parallel.children_count.store(base.children.len() as int, Relaxed);

                // Possibly enqueue the parent.
                let unsafe_parent = base.parallel.parent;
                if unsafe_parent == null_unsafe_flow() {
                    // We're done!
                    break
                }

                // No, we're not at the root yet. Then are we the last sibling of our parent? If
                // so, we can continue on with our parent; otherwise, we've gotta wait.
                let parent: &mut ~Flow = cast::transmute(&unsafe_parent);
                let parent_base = flow::mut_base(*parent);
                if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 {
                    // We were the last child of our parent. Reflow our parent.
                    unsafe_flow = unsafe_parent
                } else {
                    // Stop.
                    break
                }
            }
        }
    }
}

impl<'a> ParallelPostorderFlowTraversal for BubbleWidthsTraversal<'a> {}

impl<'a> ParallelPostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {}

fn match_and_cascade_node(unsafe_layout_node: UnsafeLayoutNode,
                          proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeLayoutNode>) {
    unsafe {
        let layout_context: &mut LayoutContext = cast::transmute(*proxy.user_data());

        // Get a real layout node.
        let node: LayoutNode = cast::transmute(unsafe_layout_node);

        if node.is_element() {
            // Perform the CSS selector matching.
            let stylist: &Stylist = cast::transmute(layout_context.stylist);
            node.match_node(stylist);

            // Perform the CSS cascade.
            let parent_opt = if OpaqueNode::from_layout_node(&node) == layout_context.reflow_root {
                None
            } else {
                node.parent_node()
            };
            node.cascade_node(parent_opt);
        }

        // Enqueue kids.
        let mut child_count = 0;
        for kid in node.children() {
            child_count += 1;

            proxy.push(WorkUnit {
                fun: match_and_cascade_node,
                data: layout_node_to_unsafe_layout_node(&kid),
            });
        }

        // Prepare for flow construction by adding this node to the leaf set or counting its
        // children.
        if child_count == 0 {
            layout_context.dom_leaf_set.access(|dom_leaf_set| dom_leaf_set.insert(&node));
        } else {
            let mut layout_data_ref = node.mutate_layout_data();
            match *layout_data_ref.get() {
                Some(ref mut layout_data) => {
                    layout_data.data.parallel.children_count.store(child_count as int, Relaxed)
                }
                None => fail!("no layout data"),
            }
        }
    }
}

fn bubble_widths(unsafe_flow: UnsafeFlow, proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
    let layout_context: &mut LayoutContext = unsafe {
        cast::transmute(*proxy.user_data())
    };
    let mut bubble_widths_traversal = BubbleWidthsTraversal {
        layout_context: layout_context,
    };
    bubble_widths_traversal.run_parallel(unsafe_flow)
}

fn assign_heights_and_store_overflow(unsafe_flow: UnsafeFlow,
                                     proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
    let layout_context: &mut LayoutContext = unsafe {
        cast::transmute(*proxy.user_data())
    };
    let mut assign_heights_traversal = AssignHeightsAndStoreOverflowTraversal {
        layout_context: layout_context,
    };
    assign_heights_traversal.run_parallel(unsafe_flow)
}

pub fn match_and_cascade_subtree(root_node: &LayoutNode,
                                 layout_context: &mut LayoutContext,
                                 queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) {
    unsafe {
        queue.data = cast::transmute(layout_context)
    }

    // Enqueue the root node.
    queue.push(WorkUnit {
        fun: match_and_cascade_node,
        data: layout_node_to_unsafe_layout_node(root_node),
    });

    queue.run();

    queue.data = ptr::mut_null()
}

pub fn traverse_flow_tree(kind: TraversalKind,
                          leaf_set: &MutexArc<FlowLeafSet>,
                          profiler_chan: ProfilerChan,
                          layout_context: &mut LayoutContext,
                          queue: &mut WorkQueue<*mut LayoutContext,UnsafeFlow>) {
    unsafe {
        queue.data = cast::transmute(layout_context)
    }

    let fun = match kind {
        BubbleWidthsTraversalKind => bubble_widths,
        AssignHeightsAndStoreOverflowTraversalKind => assign_heights_and_store_overflow,
    };

    profile(time::LayoutParallelWarmupCategory, profiler_chan, || {
        leaf_set.access(|leaf_set| {
            for &flow in leaf_set.iter() {
                queue.push(WorkUnit {
                    fun: fun,
                    data: flow,
                })
            }
        })
    });

    queue.run();

    queue.data = ptr::mut_null()
}

<commit_msg>update per comment<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/. */

//! Implements parallel traversals over the DOM and flow trees.
//!
//! This code is highly unsafe. Keep this file small and easy to audit.

use css::matching::MatchMethods;
use layout::context::LayoutContext;
use layout::flow::{Flow, FlowLeafSet, PostorderFlowTraversal};
use layout::flow;
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, BubbleWidthsTraversal};
use layout::util::{LayoutDataAccess, OpaqueNode};
use layout::wrapper::{layout_node_to_unsafe_layout_node, LayoutNode, UnsafeLayoutNode};

use extra::arc::MutexArc;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
use std::cast;
use std::ptr;
use std::sync::atomics::{AtomicInt, Relaxed, SeqCst};
use style::{Stylist, TNode};

pub enum TraversalKind {
    BubbleWidthsTraversalKind,
    AssignHeightsAndStoreOverflowTraversalKind,
}

pub type UnsafeFlow = (uint, uint);

fn null_unsafe_flow() -> UnsafeFlow {
    (0, 0)
}

pub fn owned_flow_to_unsafe_flow(flow: *~Flow) -> UnsafeFlow {
    unsafe {
        cast::transmute_copy(&*flow)
    }
}

pub fn mut_owned_flow_to_unsafe_flow(flow: *mut ~Flow) -> UnsafeFlow {
    unsafe {
        cast::transmute_copy(&*flow)
    }
}

/// Information that we need stored in each DOM node.
pub struct DomParallelInfo {
    /// The number of children that still need work done.
    children_count: AtomicInt,
}

impl DomParallelInfo {
    pub fn new() -> DomParallelInfo {
        DomParallelInfo {
            children_count: AtomicInt::new(0),
        }
    }
}

/// Information that we need stored in each flow.
pub struct FlowParallelInfo {
    /// The number of children that still need work done.
    children_count: AtomicInt,
    /// The address of the parent flow.
    parent: UnsafeFlow,
}

impl FlowParallelInfo {
    pub fn new() -> FlowParallelInfo {
        FlowParallelInfo {
            children_count: AtomicInt::new(0),
            parent: null_unsafe_flow(),
        }
    }
}

/// A parallel bottom-up flow traversal.
trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
    fn run_parallel(&mut self, mut unsafe_flow: UnsafeFlow) {
        loop {
            unsafe {
                // Get a real flow.
                let flow: &mut ~Flow = cast::transmute(&unsafe_flow);

                // Perform the appropriate traversal.
                if self.should_process(*flow) {
                    self.process(*flow);
                }

                let base = flow::mut_base(*flow);

                // Reset the count of children for the next layout traversal.
                base.parallel.children_count.store(base.children.len() as int, Relaxed);

                // Possibly enqueue the parent.
                let unsafe_parent = base.parallel.parent;
                if unsafe_parent == null_unsafe_flow() {
                    // We're done!
                    break
                }

                // No, we're not at the root yet. Then are we the last sibling of our parent? If
                // so, we can continue on with our parent; otherwise, we've gotta wait.
                let parent: &mut ~Flow = cast::transmute(&unsafe_parent);
                let parent_base = flow::mut_base(*parent);
                if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 {
                    // We were the last child of our parent. Reflow our parent.
                    unsafe_flow = unsafe_parent
                } else {
                    // Stop.
                    break
                }
            }
        }
    }
}

impl<'a> ParallelPostorderFlowTraversal for BubbleWidthsTraversal<'a> {}

impl<'a> ParallelPostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {}

fn match_and_cascade_node(unsafe_layout_node: UnsafeLayoutNode,
                          proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeLayoutNode>) {
    unsafe {
        let layout_context: &mut LayoutContext = cast::transmute(*proxy.user_data());

        // Get a real layout node.
        let node: LayoutNode = cast::transmute(unsafe_layout_node);

        if node.is_element() {
            // Perform the CSS selector matching.
            let stylist: &Stylist = cast::transmute(layout_context.stylist);
            node.match_node(stylist);

            // Perform the CSS cascade.
            let parent_opt = if OpaqueNode::from_layout_node(&node) == layout_context.reflow_root {
                None
            } else {
                node.parent_node()
            };
            node.cascade_node(parent_opt);
        }

        // Enqueue kids.
        let mut child_count = 0;
        for kid in node.children() {
            child_count += 1;

            proxy.push(WorkUnit {
                fun: match_and_cascade_node,
                data: layout_node_to_unsafe_layout_node(&kid),
            });
        }

        // Prepare for flow construction by adding this node to the leaf set or counting its
        // children.
        if child_count == 0 {
            // We don't need set the `child_count` field here since that's only used by kids during
            // bottom-up traversals, and since this node is a leaf it has no kids.
            layout_context.dom_leaf_set.access(|dom_leaf_set| dom_leaf_set.insert(&node));
        } else {
            let mut layout_data_ref = node.mutate_layout_data();
            match *layout_data_ref.get() {
                Some(ref mut layout_data) => {
                    layout_data.data.parallel.children_count.store(child_count as int, Relaxed)
                }
                None => fail!("no layout data"),
            }
        }
    }
}

fn bubble_widths(unsafe_flow: UnsafeFlow, proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
    let layout_context: &mut LayoutContext = unsafe {
        cast::transmute(*proxy.user_data())
    };
    let mut bubble_widths_traversal = BubbleWidthsTraversal {
        layout_context: layout_context,
    };
    bubble_widths_traversal.run_parallel(unsafe_flow)
}

fn assign_heights_and_store_overflow(unsafe_flow: UnsafeFlow,
                                     proxy: &mut WorkerProxy<*mut LayoutContext,UnsafeFlow>) {
    let layout_context: &mut LayoutContext = unsafe {
        cast::transmute(*proxy.user_data())
    };
    let mut assign_heights_traversal = AssignHeightsAndStoreOverflowTraversal {
        layout_context: layout_context,
    };
    assign_heights_traversal.run_parallel(unsafe_flow)
}

pub fn match_and_cascade_subtree(root_node: &LayoutNode,
                                 layout_context: &mut LayoutContext,
                                 queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) {
    unsafe {
        queue.data = cast::transmute(layout_context)
    }

    // Enqueue the root node.
    queue.push(WorkUnit {
        fun: match_and_cascade_node,
        data: layout_node_to_unsafe_layout_node(root_node),
    });

    queue.run();

    queue.data = ptr::mut_null()
}

pub fn traverse_flow_tree(kind: TraversalKind,
                          leaf_set: &MutexArc<FlowLeafSet>,
                          profiler_chan: ProfilerChan,
                          layout_context: &mut LayoutContext,
                          queue: &mut WorkQueue<*mut LayoutContext,UnsafeFlow>) {
    unsafe {
        queue.data = cast::transmute(layout_context)
    }

    let fun = match kind {
        BubbleWidthsTraversalKind => bubble_widths,
        AssignHeightsAndStoreOverflowTraversalKind => assign_heights_and_store_overflow,
    };

    profile(time::LayoutParallelWarmupCategory, profiler_chan, || {
        leaf_set.access(|leaf_set| {
            for &flow in leaf_set.iter() {
                queue.push(WorkUnit {
                    fun: fun,
                    data: flow,
                })
            }
        })
    });

    queue.run();

    queue.data = ptr::mut_null()
}

<|endoftext|>


<commit_before>use ascii_char::{AsciiChar, ToAsciiChar};

/// Terminals use [caret notation](https://en.wikipedia.org/wiki/Caret_notation)
/// to display some typed control codes, such as ^D for EOT and ^Z for SUB.
///
/// This function returns the caret notation letter for control codes,
/// or `None` for printable characters.
///
/// # Examples
/// ```
/// # use ascii::{AsciiChar, caret_encode};
/// assert_eq!(caret_encode(b'\0'), Some(AsciiChar::At));
/// assert_eq!(caret_encode(AsciiChar::DEL), Some(AsciiChar::Question));
/// assert_eq!(caret_encode(b'E'), None);
/// assert_eq!(caret_encode(b'\n'), Some(AsciiChar::J));
/// ```
pub fn caret_encode<C: Copy + Into<u8>>(c: C) -> Option<AsciiChar> {
    // The formula is explained in the Wikipedia article.
    let c = c.into() ^ 0b0100_0000;
    unsafe {
        if c >= b'?' && c <= b'_' {
            Some(c.to_ascii_char_unchecked())
        } else {
            None
        }
    }
}

/// Returns the control code represented by a [caret notation](https://en.wikipedia.org/wiki/Caret_notation)
/// letter, or `None` if the letter is not used in caret notation.
///
/// This function is the inverse of `caret_encode()`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ascii::{AsciiChar, caret_decode};
/// assert_eq!(caret_decode(b'?'), Some(AsciiChar::DEL));
/// assert_eq!(caret_decode(AsciiChar::D), Some(AsciiChar::EOT));
/// assert_eq!(caret_decode(b'\0'), None);
/// ```
///
/// Symmetry:
///
/// ```
/// # use ascii::{AsciiChar, caret_encode, caret_decode};
/// assert_eq!(caret_encode(AsciiChar::US).and_then(caret_decode), Some(AsciiChar::US));
/// assert_eq!(caret_decode(b'@').and_then(caret_encode), Some(AsciiChar::At));
/// ```
pub fn caret_decode<C: Copy + Into<u8>>(c: C) -> Option<AsciiChar> {
    // The formula is explained in the Wikipedia article.
    unsafe {
        match c.into() {
            b'?'..=b'_' => Some(AsciiChar::from_ascii_unchecked(c.into() ^ 0b0100_0000)),
            _ => None,
        }
    }
}
<commit_msg>Documented all unsafe usages in `free_functions` module.<commit_after>use ascii_char::{AsciiChar, ToAsciiChar};

/// Terminals use [caret notation](https://en.wikipedia.org/wiki/Caret_notation)
/// to display some typed control codes, such as ^D for EOT and ^Z for SUB.
///
/// This function returns the caret notation letter for control codes,
/// or `None` for printable characters.
///
/// # Examples
/// ```
/// # use ascii::{AsciiChar, caret_encode};
/// assert_eq!(caret_encode(b'\0'), Some(AsciiChar::At));
/// assert_eq!(caret_encode(AsciiChar::DEL), Some(AsciiChar::Question));
/// assert_eq!(caret_encode(b'E'), None);
/// assert_eq!(caret_encode(b'\n'), Some(AsciiChar::J));
/// ```
pub fn caret_encode<C: Copy + Into<u8>>(c: C) -> Option<AsciiChar> {
    // The formula is explained in the Wikipedia article.
    let c = c.into() ^ 0b0100_0000;
    if c >= b'?' && c <= b'_' {
        // SAFETY: All bytes between '?' (0x3F) and '_' (0x5f) are valid ascii characters.
        Some(unsafe { c.to_ascii_char_unchecked() })
    } else {
        None
    }
}

/// Returns the control code represented by a [caret notation](https://en.wikipedia.org/wiki/Caret_notation)
/// letter, or `None` if the letter is not used in caret notation.
///
/// This function is the inverse of `caret_encode()`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use ascii::{AsciiChar, caret_decode};
/// assert_eq!(caret_decode(b'?'), Some(AsciiChar::DEL));
/// assert_eq!(caret_decode(AsciiChar::D), Some(AsciiChar::EOT));
/// assert_eq!(caret_decode(b'\0'), None);
/// ```
///
/// Symmetry:
///
/// ```
/// # use ascii::{AsciiChar, caret_encode, caret_decode};
/// assert_eq!(caret_encode(AsciiChar::US).and_then(caret_decode), Some(AsciiChar::US));
/// assert_eq!(caret_decode(b'@').and_then(caret_encode), Some(AsciiChar::At));
/// ```
pub fn caret_decode<C: Copy + Into<u8>>(c: C) -> Option<AsciiChar> {
    // The formula is explained in the Wikipedia article.
    match c.into() {
        // SAFETY: All bytes between '?' (0x3F) and '_' (0x5f) after `xoring` with `0b0100_0000` are
        //         valid bytes, as they represent characters between '␀' (0x0) and '␠' (0x1f) + '␡' (0x7f)
        b'?'..=b'_' => Some(unsafe { AsciiChar::from_ascii_unchecked(c.into() ^ 0b0100_0000) }),
        _ => None,
    }
}
<|endoftext|>


<commit_before>use super::ease::Easing;
pub struct Expo;

impl Easing for Expo {
    fn ease_in(t: f32, b: f32, c: f32, d: f32) -> f32 {
        if t == 0.0 {
            b
        } else {
            c * 2_f32.powf(10.0 * (t / d - 1.0)) + b
        }
    }

    fn ease_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        if t == d {
            b + c
        } else {
            c * (-2_f32.powf(-10.0 * t / d) + 1.0) + b
        }
    }

    fn ease_in_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        if t == 0.0 {
            return b;
        }
        if t == d {
            return b + c;
        }
        let mut inner_t = t / (d / 2.0);
        if inner_t < 1.0 {
            return c / 2.0 * 2_f32.powf(10.0 * (inner_t - 1.0)) + b;
        }
        inner_t -= 1.0;
        return c / 2.0 * (-(2_f32.powf(-10.0 * inner_t)) + 2.0) + b;
    }
}

mod test {
    use functions::ease::Easing;
    #[test]
    fn ease_in() {
        assert_relative_eq!(super::Expo::ease_in(1.0, 2.0, 3.0, 4.0), 2.016573);
    }

    #[test]
    fn ease_out() {
        assert_relative_eq!(super::Expo::ease_out(1.0, 2.0, 3.0, 4.0), 4.469670);
    }

    #[test]
    fn ease_in_out() {
        assert_relative_eq!(super::Expo::ease_in_out(1.0, 2.0, 3.0, 4.0), 2.046875);
    }

}
<commit_msg>more expo function test added<commit_after>use super::ease::Easing;
pub struct Expo;

impl Easing for Expo {
    fn ease_in(t: f32, b: f32, c: f32, d: f32) -> f32 {
        if t == 0.0 {
            b
        } else {
            c * 2_f32.powf(10.0 * (t / d - 1.0)) + b
        }
    }

    fn ease_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        if t == d {
            b + c
        } else {
            c * (-2_f32.powf(-10.0 * t / d) + 1.0) + b
        }
    }

    fn ease_in_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        if t == 0.0 {
            return b;
        }
        if t == d {
            return b + c;
        }
        let mut inner_t = t / (d / 2.0);
        if inner_t < 1.0 {
            return c / 2.0 * 2_f32.powf(10.0 * (inner_t - 1.0)) + b;
        }
        inner_t -= 1.0;
        return c / 2.0 * (-(2_f32.powf(-10.0 * inner_t)) + 2.0) + b;
    }
}

mod test {
    use functions::ease::Easing;
    #[test]
    fn ease_in() {
        assert_relative_eq!(super::Expo::ease_in(1.0, 2.0, 3.0, 4.0), 2.016573);
        assert_relative_eq!(super::Expo::ease_in(0.0,1.0,100.0,100.0), 1.000000);
    }

    #[test]
    fn ease_out() {
        assert_relative_eq!(super::Expo::ease_out(1.0, 2.0, 3.0, 4.0), 4.469670);
        assert_relative_eq!(super::Expo::ease_out(100.0,1.0,100.0,100.0), 101.0000);
    }

    #[test]
    fn ease_in_out() {
        assert_relative_eq!(super::Expo::ease_in_out(1.0, 2.0, 3.0, 4.0), 2.046875);
        assert_relative_eq!(super::Expo::ease_in_out(0.0,1.0,100.0,100.0), 1.0000);
        assert_relative_eq!(super::Expo::ease_in_out(100.0,1.0,100.0,100.0), 101.000);
        assert_relative_eq!(super::Expo::ease_in_out(51.0,1.0,100.0,100.0), 57.472466);
    }

}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::atomic::Ordering;

use ctxt::SemContext;
use gc::root::IndirectObj;
use gc::space::Space;
use gc::swiper::card::CardTable;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::Region;
use gc::swiper::CARD_SIZE;
use gc::swiper::{formatted_size, on_different_cards};
use gc::Address;
use mem;
use object::{offset_of_array_data, Obj};
use os;
use timer::{in_ms, Timer};

pub struct FullCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    heap: Region,
    young: &'a YoungGen,
    old: &'a OldGen,
    large_space: &'a LargeSpace,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    perm_space: &'a Space,

    marking_bitmap: MarkingBitmap,
    fwd_table: ForwardTable,

    fwd: Address,
    fwd_end: Address,
    old_top: Address,
}

impl<'a, 'ast> FullCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        heap: Region,
        young: &'a YoungGen,
        old: &'a OldGen,
        large_space: &'a LargeSpace,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        perm_space: &'a Space,
        rootset: &'a [IndirectObj],
    ) -> FullCollector<'a, 'ast> {
        let marking_bitmap = MarkingBitmap::new(heap.clone());

        FullCollector {
            ctxt: ctxt,
            heap: heap,
            young: young,
            old: old,
            large_space: large_space,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            perm_space: perm_space,

            marking_bitmap: marking_bitmap,
            fwd_table: ForwardTable::new(),

            fwd: old.total.start,
            fwd_end: old.total.end,
            old_top: Address::null(),
        }
    }

    pub fn collect(&mut self) {
        let active = self.ctxt.args.flag_gc_verbose;
        let mut timer = Timer::new(active);
        let init_size = self.heap_size();

        let time_mark = Timer::ms(active, || {
            self.mark_live();
        });

        let time_forward = Timer::ms(active, || {
            self.compute_forward();
        });

        let time_updateref = Timer::ms(active, || {
            self.update_references();
        });

        let time_relocate = Timer::ms(active, || {
            self.relocate();
        });

        let time_large = Timer::ms(active, || {
            self.update_large_objects();
        });

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let garbage = init_size - new_size;
            let garbage_ratio = if init_size == 0 {
                0f64
            } else {
                (garbage as f64 / init_size as f64) * 100f64
            };

            println!(
                "GC: Full GC ({:.2} ms, {}->{} size, {}/{:.0}% garbage); \
                 mark={:.1}ms forward={:.1}ms updateref={:.1}ms relocate={:.1}ms large={:.1}ms",
                in_ms(dur),
                formatted_size(init_size),
                formatted_size(new_size),
                formatted_size(garbage),
                garbage_ratio,
                time_mark,
                time_forward,
                time_updateref,
                time_relocate,
                time_large,
            );
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn mark_live(&mut self) {
        let mut marking_stack: Vec<Address> = Vec::new();
        let mut live_objects = 0;

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if self.heap.contains(root_ptr) {
                if !self.is_marked_addr(root_ptr) {
                    marking_stack.push(root_ptr);
                    self.mark(root_ptr);
                    live_objects += 1;
                }
            } else {
                debug_assert!(root_ptr.is_null() || self.perm_space.contains(root_ptr));
            }
        }

        while marking_stack.len() > 0 {
            let object_addr = marking_stack.pop().expect("stack already empty");
            let object = unsafe { &mut *object_addr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_addr = Address::from_ptr(field.get());

                if self.heap.contains(field_addr) {
                    if !self.is_marked_addr(field_addr) {
                        marking_stack.push(field_addr);
                        self.mark(field_addr);
                        live_objects += 1;
                    }
                } else {
                    debug_assert!(field_addr.is_null() || self.perm_space.contains(field_addr));
                }
            });
        }

        self.fwd_table.reserve_capacity(live_objects);
    }

    fn compute_forward(&mut self) {
        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let fwd = full.allocate(object_size);
                full.fwd_table.forward_to(address, fwd);
            }
        });
    }

    fn update_references(&mut self) {
        self.walk_old_and_young(|full, object, _address, _| {
            if full.is_marked(object) {
                object.visit_reference_fields(|field| {
                    full.forward_reference(field);
                });
            }
        });

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if !root_ptr.is_null()
                && !self.perm_space.contains(root_ptr)
                && !self.large_space.contains(root_ptr)
            {
                let fwd_addr = self.fwd_table.forward_address(root_ptr);
                root.set(fwd_addr.to_mut_ptr());
            }
        }
    }

    fn relocate(&mut self) {
        self.young.unprotect_to_space();

        self.crossing_map.set_first_object(0.into(), 0);
        let mut last_dest = Address::null();

        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let dest = full.fwd_table.forward_address(address);
                object.copy_to(dest, object_size);

                let next_dest = dest.offset(object_size);

                if on_different_cards(dest, next_dest) && full.old.contains(dest) {
                    full.update_crossing(dest, next_dest, object.is_array_ref());
                }

                last_dest = next_dest;
            }
        });

        let young_top;
        let old_top;

        // check if we have left the old-generation into
        // the young generation for copying objects
        if self.old_top.is_null() {
            // if not, young gen is empty
            young_top = self.young.to_space().start;
            old_top = self.fwd;
        } else {
            young_top = self.fwd;
            old_top = self.old_top;
        }

        debug_assert!(self.young.valid_top(young_top));
        self.young.swap_spaces(young_top);
        self.young.protect_to_space();

        debug_assert!(self.old.valid_top(old_top));
        self.old.free.store(old_top.to_usize(), Ordering::SeqCst);
    }

    fn update_large_objects(&mut self) {
        self.large_space.remove_objects(|object_start| {
            if !self.is_marked_addr(object_start) {
                // free object
                return false;
            }

            let object = unsafe { &mut *object_start.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                self.forward_reference(field);
            });

            let object_end = object_start.offset(object.size());
            self.card_table.reset_region(object_start, object_end);

            // keep object
            true
        });
    }

    fn forward_reference(&mut self, indirect_obj: IndirectObj) {
        let object_addr = Address::from_ptr(indirect_obj.get());

        if !object_addr.is_null()
            && !self.perm_space.contains(object_addr)
            && !self.large_space.contains(object_addr)
        {
            let fwd_addr = self.fwd_table.forward_address(object_addr);
            indirect_obj.set(fwd_addr.to_mut_ptr());
        }
    }

    fn walk_old_and_young<F>(&mut self, mut fct: F)
    where
        F: FnMut(&mut FullCollector, &mut Obj, Address, usize),
    {
        let used_region = self.old.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);

        let used_region = self.young.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);
    }

    fn walk_region<F>(&mut self, start: Address, end: Address, fct: &mut F)
    where
        F: FnMut(&mut FullCollector, &mut Obj, Address, usize),
    {
        let mut scan = start;

        while scan < end {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };
            let object_size = object.size();

            fct(self, object, scan, object_size);

            scan = scan.offset(object_size);
        }
    }

    fn allocate(&mut self, object_size: usize) -> Address {
        let addr = self.fwd;
        let next = self.fwd.offset(object_size);

        if next <= self.fwd_end {
            self.fwd = next;
            return addr;
        }

        panic!("FAIL: Not enough space for objects in old generation.");
    }

    fn update_crossing(&mut self, last: Address, addr: Address, array_ref: bool) {
        let last_card = self.card_table.card_idx(last);

        let offset = addr.to_usize() & (CARD_SIZE - 1);
        let offset_words = offset / mem::ptr_width_usize();

        let card = self.card_table.card_idx(addr);

        if array_ref {
            let last_card_end = self.card_table.to_address(last_card).offset(CARD_SIZE);
            let loop_start;

            if last.offset(offset_of_array_data() as usize) > last_card_end {
                let diff_words = last_card_end.offset_from(last) / mem::ptr_width_usize();
                self.crossing_map
                    .set_array_start((last_card.to_usize() + 1).into(), diff_words);

                loop_start = last_card.to_usize() + 2;
            } else {
                loop_start = last_card.to_usize() + 1;
            }

            let refs_per_card = CARD_SIZE / mem::ptr_width_usize();

            for i in loop_start..card.to_usize() {
                self.crossing_map
                    .set_references_at_start(i.into(), refs_per_card);
            }

            if card.to_usize() >= loop_start {
                self.crossing_map
                    .set_references_at_start(card, offset_words);
            }
        } else {
            for i in last_card.to_usize() + 1..card.to_usize() {
                self.crossing_map.set_no_references(i.into());
            }

            self.crossing_map.set_first_object(card, offset_words);
        }
    }

    fn is_marked(&self, obj: &Obj) -> bool {
        self.marking_bitmap
            .is_marked(Address::from_ptr(obj as *const _))
    }

    fn is_marked_addr(&self, addr: Address) -> bool {
        self.marking_bitmap.is_marked(addr)
    }

    fn mark(&mut self, addr: Address) {
        self.marking_bitmap.mark(addr);
    }
}

pub struct MarkingBitmap {
    heap: Region,
    bitmap: Region,
}

impl MarkingBitmap {
    pub fn new(heap: Region) -> MarkingBitmap {
        let bitmap_size = mem::page_align(heap.size() / mem::ptr_width_usize() / 8);
        let bitmap_start = Address::from_ptr(os::mmap(bitmap_size, os::Writable));

        if bitmap_start.is_null() {
            panic!("could not allocate marking bitmap.");
        }

        MarkingBitmap {
            heap: heap,
            bitmap: Region::new(bitmap_start, bitmap_start.offset(bitmap_size)),
        }
    }

    pub fn mark(&mut self, addr: Address) {
        let (byte_offset, bit_offset) = self.mark_offset(addr);

        let byte = self.mark_byte(byte_offset);
        let modified_byte = byte | (1 << bit_offset);
        self.set_mark_byte(byte_offset, modified_byte);
    }

    pub fn is_marked(&self, addr: Address) -> bool {
        let (byte_offset, bit_offset) = self.mark_offset(addr);
        let byte = self.mark_byte(byte_offset);

        byte & (1 << bit_offset) != 0
    }

    fn mark_offset(&self, addr: Address) -> (usize, u8) {
        debug_assert!(self.heap.contains(addr));
        let offset = addr.offset_from(self.heap.start) / mem::ptr_width_usize();
        let byte = offset / 8;
        let bit = offset % 8;

        (byte, bit as u8)
    }

    fn mark_byte(&self, offset: usize) -> u8 {
        let addr = self.bitmap.start.offset(offset);

        unsafe { *addr.to_ptr() }
    }

    fn set_mark_byte(&self, offset: usize, val: u8) {
        let addr = self.bitmap.start.offset(offset);

        unsafe {
            *addr.to_mut_ptr() = val;
        }
    }
}

impl Drop for MarkingBitmap {
    fn drop(&mut self) {
        os::munmap(self.bitmap.start.to_ptr(), self.bitmap.size());
    }
}

struct ForwardTable {
    data: HashMap<Address, Address>,
}

impl ForwardTable {
    fn new() -> ForwardTable {
        ForwardTable {
            data: HashMap::new(),
        }
    }

    fn reserve_capacity(&mut self, count: usize) {
        self.data.reserve(count);
    }

    fn forward_to(&mut self, addr: Address, fwd: Address) {
        self.data.insert(addr, fwd);
    }

    fn forward_address(&mut self, addr: Address) -> Address {
        *self.data.get(&addr).expect("no forward address found.")
    }
}
<commit_msg>swiper: always update crossing<commit_after>use std::collections::HashMap;
use std::sync::atomic::Ordering;

use ctxt::SemContext;
use gc::root::IndirectObj;
use gc::space::Space;
use gc::swiper::card::CardTable;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::Region;
use gc::swiper::CARD_SIZE;
use gc::swiper::{formatted_size, on_different_cards};
use gc::Address;
use mem;
use object::{offset_of_array_data, Obj};
use os;
use timer::{in_ms, Timer};

pub struct FullCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    heap: Region,
    young: &'a YoungGen,
    old: &'a OldGen,
    large_space: &'a LargeSpace,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    perm_space: &'a Space,

    marking_bitmap: MarkingBitmap,
    fwd_table: ForwardTable,

    fwd: Address,
    fwd_end: Address,
    old_top: Address,
}

impl<'a, 'ast> FullCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        heap: Region,
        young: &'a YoungGen,
        old: &'a OldGen,
        large_space: &'a LargeSpace,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        perm_space: &'a Space,
        rootset: &'a [IndirectObj],
    ) -> FullCollector<'a, 'ast> {
        let marking_bitmap = MarkingBitmap::new(heap.clone());

        FullCollector {
            ctxt: ctxt,
            heap: heap,
            young: young,
            old: old,
            large_space: large_space,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            perm_space: perm_space,

            marking_bitmap: marking_bitmap,
            fwd_table: ForwardTable::new(),

            fwd: old.total.start,
            fwd_end: old.total.end,
            old_top: Address::null(),
        }
    }

    pub fn collect(&mut self) {
        let active = self.ctxt.args.flag_gc_verbose;
        let mut timer = Timer::new(active);
        let init_size = self.heap_size();

        let time_mark = Timer::ms(active, || {
            self.mark_live();
        });

        let time_forward = Timer::ms(active, || {
            self.compute_forward();
        });

        let time_updateref = Timer::ms(active, || {
            self.update_references();
        });

        let time_relocate = Timer::ms(active, || {
            self.relocate();
        });

        let time_large = Timer::ms(active, || {
            self.update_large_objects();
        });

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let garbage = init_size - new_size;
            let garbage_ratio = if init_size == 0 {
                0f64
            } else {
                (garbage as f64 / init_size as f64) * 100f64
            };

            println!(
                "GC: Full GC ({:.2} ms, {}->{} size, {}/{:.0}% garbage); \
                 mark={:.1}ms forward={:.1}ms updateref={:.1}ms relocate={:.1}ms large={:.1}ms",
                in_ms(dur),
                formatted_size(init_size),
                formatted_size(new_size),
                formatted_size(garbage),
                garbage_ratio,
                time_mark,
                time_forward,
                time_updateref,
                time_relocate,
                time_large,
            );
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn mark_live(&mut self) {
        let mut marking_stack: Vec<Address> = Vec::new();
        let mut live_objects = 0;

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if self.heap.contains(root_ptr) {
                if !self.is_marked_addr(root_ptr) {
                    marking_stack.push(root_ptr);
                    self.mark(root_ptr);
                    live_objects += 1;
                }
            } else {
                debug_assert!(root_ptr.is_null() || self.perm_space.contains(root_ptr));
            }
        }

        while marking_stack.len() > 0 {
            let object_addr = marking_stack.pop().expect("stack already empty");
            let object = unsafe { &mut *object_addr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_addr = Address::from_ptr(field.get());

                if self.heap.contains(field_addr) {
                    if !self.is_marked_addr(field_addr) {
                        marking_stack.push(field_addr);
                        self.mark(field_addr);
                        live_objects += 1;
                    }
                } else {
                    debug_assert!(field_addr.is_null() || self.perm_space.contains(field_addr));
                }
            });
        }

        self.fwd_table.reserve_capacity(live_objects);
    }

    fn compute_forward(&mut self) {
        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let fwd = full.allocate(object_size);
                full.fwd_table.forward_to(address, fwd);
            }
        });
    }

    fn update_references(&mut self) {
        self.walk_old_and_young(|full, object, _address, _| {
            if full.is_marked(object) {
                object.visit_reference_fields(|field| {
                    full.forward_reference(field);
                });
            }
        });

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if !root_ptr.is_null()
                && !self.perm_space.contains(root_ptr)
                && !self.large_space.contains(root_ptr)
            {
                let fwd_addr = self.fwd_table.forward_address(root_ptr);
                root.set(fwd_addr.to_mut_ptr());
            }
        }
    }

    fn relocate(&mut self) {
        self.young.unprotect_to_space();

        self.crossing_map.set_first_object(0.into(), 0);
        let mut last_dest = Address::null();

        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let dest = full.fwd_table.forward_address(address);
                object.copy_to(dest, object_size);

                let next_dest = dest.offset(object_size);

                if on_different_cards(dest, next_dest) {
                    full.update_crossing(dest, next_dest, object.is_array_ref());
                }

                last_dest = next_dest;
            }
        });

        let young_top;
        let old_top;

        // check if we have left the old-generation into
        // the young generation for copying objects
        if self.old_top.is_null() {
            // if not, young gen is empty
            young_top = self.young.to_space().start;
            old_top = self.fwd;
        } else {
            young_top = self.fwd;
            old_top = self.old_top;
        }

        debug_assert!(self.young.valid_top(young_top));
        self.young.swap_spaces(young_top);
        self.young.protect_to_space();

        debug_assert!(self.old.valid_top(old_top));
        self.old.free.store(old_top.to_usize(), Ordering::SeqCst);
    }

    fn update_large_objects(&mut self) {
        self.large_space.remove_objects(|object_start| {
            if !self.is_marked_addr(object_start) {
                // free object
                return false;
            }

            let object = unsafe { &mut *object_start.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                self.forward_reference(field);
            });

            let object_end = object_start.offset(object.size());
            self.card_table.reset_region(object_start, object_end);

            // keep object
            true
        });
    }

    fn forward_reference(&mut self, indirect_obj: IndirectObj) {
        let object_addr = Address::from_ptr(indirect_obj.get());

        if !object_addr.is_null()
            && !self.perm_space.contains(object_addr)
            && !self.large_space.contains(object_addr)
        {
            let fwd_addr = self.fwd_table.forward_address(object_addr);
            indirect_obj.set(fwd_addr.to_mut_ptr());
        }
    }

    fn walk_old_and_young<F>(&mut self, mut fct: F)
    where
        F: FnMut(&mut FullCollector, &mut Obj, Address, usize),
    {
        let used_region = self.old.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);

        let used_region = self.young.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);
    }

    fn walk_region<F>(&mut self, start: Address, end: Address, fct: &mut F)
    where
        F: FnMut(&mut FullCollector, &mut Obj, Address, usize),
    {
        let mut scan = start;

        while scan < end {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };
            let object_size = object.size();

            fct(self, object, scan, object_size);

            scan = scan.offset(object_size);
        }
    }

    fn allocate(&mut self, object_size: usize) -> Address {
        let addr = self.fwd;
        let next = self.fwd.offset(object_size);

        if next <= self.fwd_end {
            self.fwd = next;
            return addr;
        }

        panic!("FAIL: Not enough space for objects in old generation.");
    }

    fn update_crossing(&mut self, last: Address, addr: Address, array_ref: bool) {
        let last_card = self.card_table.card_idx(last);

        let offset = addr.to_usize() & (CARD_SIZE - 1);
        let offset_words = offset / mem::ptr_width_usize();

        let card = self.card_table.card_idx(addr);

        if array_ref {
            let last_card_end = self.card_table.to_address(last_card).offset(CARD_SIZE);
            let loop_start;

            if last.offset(offset_of_array_data() as usize) > last_card_end {
                let diff_words = last_card_end.offset_from(last) / mem::ptr_width_usize();
                self.crossing_map
                    .set_array_start((last_card.to_usize() + 1).into(), diff_words);

                loop_start = last_card.to_usize() + 2;
            } else {
                loop_start = last_card.to_usize() + 1;
            }

            let refs_per_card = CARD_SIZE / mem::ptr_width_usize();

            for i in loop_start..card.to_usize() {
                self.crossing_map
                    .set_references_at_start(i.into(), refs_per_card);
            }

            if card.to_usize() >= loop_start {
                self.crossing_map
                    .set_references_at_start(card, offset_words);
            }
        } else {
            for i in last_card.to_usize() + 1..card.to_usize() {
                self.crossing_map.set_no_references(i.into());
            }

            self.crossing_map.set_first_object(card, offset_words);
        }
    }

    fn is_marked(&self, obj: &Obj) -> bool {
        self.marking_bitmap
            .is_marked(Address::from_ptr(obj as *const _))
    }

    fn is_marked_addr(&self, addr: Address) -> bool {
        self.marking_bitmap.is_marked(addr)
    }

    fn mark(&mut self, addr: Address) {
        self.marking_bitmap.mark(addr);
    }
}

pub struct MarkingBitmap {
    heap: Region,
    bitmap: Region,
}

impl MarkingBitmap {
    pub fn new(heap: Region) -> MarkingBitmap {
        let bitmap_size = mem::page_align(heap.size() / mem::ptr_width_usize() / 8);
        let bitmap_start = Address::from_ptr(os::mmap(bitmap_size, os::Writable));

        if bitmap_start.is_null() {
            panic!("could not allocate marking bitmap.");
        }

        MarkingBitmap {
            heap: heap,
            bitmap: Region::new(bitmap_start, bitmap_start.offset(bitmap_size)),
        }
    }

    pub fn mark(&mut self, addr: Address) {
        let (byte_offset, bit_offset) = self.mark_offset(addr);

        let byte = self.mark_byte(byte_offset);
        let modified_byte = byte | (1 << bit_offset);
        self.set_mark_byte(byte_offset, modified_byte);
    }

    pub fn is_marked(&self, addr: Address) -> bool {
        let (byte_offset, bit_offset) = self.mark_offset(addr);
        let byte = self.mark_byte(byte_offset);

        byte & (1 << bit_offset) != 0
    }

    fn mark_offset(&self, addr: Address) -> (usize, u8) {
        debug_assert!(self.heap.contains(addr));
        let offset = addr.offset_from(self.heap.start) / mem::ptr_width_usize();
        let byte = offset / 8;
        let bit = offset % 8;

        (byte, bit as u8)
    }

    fn mark_byte(&self, offset: usize) -> u8 {
        let addr = self.bitmap.start.offset(offset);

        unsafe { *addr.to_ptr() }
    }

    fn set_mark_byte(&self, offset: usize, val: u8) {
        let addr = self.bitmap.start.offset(offset);

        unsafe {
            *addr.to_mut_ptr() = val;
        }
    }
}

impl Drop for MarkingBitmap {
    fn drop(&mut self) {
        os::munmap(self.bitmap.start.to_ptr(), self.bitmap.size());
    }
}

struct ForwardTable {
    data: HashMap<Address, Address>,
}

impl ForwardTable {
    fn new() -> ForwardTable {
        ForwardTable {
            data: HashMap::new(),
        }
    }

    fn reserve_capacity(&mut self, count: usize) {
        self.data.reserve(count);
    }

    fn forward_to(&mut self, addr: Address, fwd: Address) {
        self.data.insert(addr, fwd);
    }

    fn forward_address(&mut self, addr: Address) -> Address {
        *self.data.get(&addr).expect("no forward address found.")
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::atomic::Ordering;

use ctxt::SemContext;
use gc::Address;
use gc::root::IndirectObj;
use gc::space::Space;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::CARD_SIZE;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::{in_kilo, on_different_cards, start_of_card};
use gc::swiper::old::OldGen;
use gc::swiper::Region;
use gc::swiper::young::YoungGen;
use mem;
use object::Obj;
use os;
use timer::{in_ms, Timer};

pub struct FullCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    heap: Region,
    young: &'a YoungGen,
    old: &'a OldGen,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    perm_space: &'a Space,

    marking_bitmap: MarkingBitmap,
    fwd_table: ForwardTable,

    fwd: Address,
    fwd_end: Address,
    old_top: Address,
}

impl<'a, 'ast> FullCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        heap: Region,
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        perm_space: &'a Space,
        rootset: &'a [IndirectObj],
    ) -> FullCollector<'a, 'ast> {
        let marking_bitmap = MarkingBitmap::new(heap.clone());

        FullCollector {
            ctxt: ctxt,
            heap: heap,
            young: young,
            old: old,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            perm_space: perm_space,

            marking_bitmap: marking_bitmap,
            fwd_table: ForwardTable::new(),

            fwd: old.total.start,
            fwd_end: old.total.end,
            old_top: Address::null(),
        }
    }

    pub fn collect(&mut self) {
        let mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        let init_size = self.heap_size();

        self.mark_live();
        self.compute_forward();
        self.update_references();
        self.relocate();

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let garbage = init_size - new_size;
            let garbage_ratio = (garbage as f64 / init_size as f64) * 100f64;

            println!("GC: Full GC ({:.2} ms, {:.1}K->{:.1}K size, {:.1}K/{:.0}% garbage)",
                in_ms(dur), in_kilo(init_size), in_kilo(new_size),
                in_kilo(garbage), garbage_ratio);
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn mark_live(&mut self) {
        let mut marking_stack: Vec<Address> = Vec::new();
        let mut live_objects = 0;

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if self.heap.contains(root_ptr) {
                if !self.is_marked_addr(root_ptr) {
                    marking_stack.push(root_ptr);
                    self.mark(root_ptr);
                    live_objects += 1;
                }

            } else {
                debug_assert!(root_ptr.is_null() || self.perm_space.contains(root_ptr));
            }
        }

        while marking_stack.len() > 0 {
            let object_addr = marking_stack.pop().expect("stack already empty");
            let object = unsafe { &mut *object_addr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_addr = Address::from_ptr(field.get());

                if self.heap.contains(field_addr) {
                    if !self.is_marked_addr(field_addr) {
                        marking_stack.push(field_addr);
                        self.mark(field_addr);
                        live_objects += 1;
                    }

                } else {
                    debug_assert!(field_addr.is_null() || self.perm_space.contains(field_addr));
                }
            });
        }

        self.fwd_table.reserve_capacity(live_objects);
    }

    fn compute_forward(&mut self) {
        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let fwd = full.allocate(object_size);
                full.fwd_table.forward_to(address, fwd);
            }
        });
    }

    fn update_references(&mut self) {
        self.walk_old_and_young(|full, object, address, _| {
            if full.is_marked(object) {
                let mut young_refs = false;

                object.visit_reference_fields(|field| {
                    let field_addr = Address::from_ptr(field.get());

                    if !field_addr.is_null() && !full.perm_space.contains(field_addr) {
                        let fwd_addr = full.fwd_table.forward_address(field_addr);
                        field.set(fwd_addr.to_mut_ptr());

                        if full.young.contains(fwd_addr) {
                            young_refs = true;
                        }
                    }
                });

                if young_refs {
                    full.fwd_table.set_young_refs(address);
                }
            }
        });

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if !root_ptr.is_null() && !self.perm_space.contains(root_ptr) {
                let fwd_addr = self.fwd_table.forward_address(root_ptr);
                root.set(fwd_addr.to_mut_ptr());
            }
        }
    }

    fn relocate(&mut self) {
        self.young.unprotect_to_space();

        self.crossing_map.set_first_object(0.into(), 0);
        let mut young_refs = false;
        let mut last_dest = Address::null();

        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let dest = full.fwd_table.forward_address(address);
                object.copy_to(dest, object_size);

                if !young_refs {
                    young_refs = full.fwd_table.has_young_refs(address);
                }

                let next_dest = dest.offset(object_size);

                if on_different_cards(dest, next_dest) && full.old.contains(dest) {
                    full.update_crossing(next_dest);
                    full.update_card(dest, &mut young_refs);
                }

                last_dest = next_dest;
            }
        });

        if !last_dest.is_null() && !start_of_card(last_dest) && self.old.contains(last_dest) {
            self.update_card(last_dest, &mut young_refs);
        }

        let young_top;
        let old_top;

        // check if we have left the old-generation into
        // the young generation for copying objects
        if self.old_top.is_null() {
            // if not, young gen is empty
            young_top = self.young.to_space().start;
            old_top = self.fwd;

        } else {
            young_top = self.fwd;
            old_top = self.old_top;
        }

        debug_assert!(self.young.valid_top(young_top));
        self.young.swap_spaces(young_top);
        self.young.protect_to_space();

        debug_assert!(self.old.valid_top(old_top));
        self.old.free.store(old_top.to_usize(), Ordering::SeqCst);
    }

    fn walk_old_and_young<F>(&mut self, mut fct: F) where F: FnMut(&mut FullCollector, &mut Obj, Address, usize) {
        let used_region = self.old.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);

        let used_region = self.young.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);
    }

    fn walk_region<F>(&mut self, start: Address, end: Address, fct: &mut F) where F: FnMut(&mut FullCollector, &mut Obj, Address, usize) {
        let mut scan = start;

        while scan < end {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };
            let object_size = object.size();

            fct(self, object, scan, object_size);

            scan = scan.offset(object_size);
        }
    }

    fn allocate(&mut self, object_size: usize) -> Address {
        let addr = self.fwd;
        let next = self.fwd.offset(object_size);

        if next <= self.fwd_end {
            self.fwd = next;
            return addr;
        }

        assert!(self.fwd_end == self.old.total.end);
        assert!(self.old_top.is_null());
        self.old_top = self.fwd;

        let young = self.young.to_space();
        self.fwd = young.start.offset(object_size);
        self.fwd_end = young.end;

        young.start
    }

    fn update_crossing(&mut self, addr: Address) {
        let offset = addr.to_usize() & (CARD_SIZE - 1);
        let offset_words = offset / (mem::ptr_width() as usize);

        let card = self.old.card_from_address(addr);

        self.crossing_map.set_first_object(card, offset_words);
    }

    fn update_card(&mut self, addr: Address, young_refs: &mut bool) {
        let card = self.old.card_from_address(addr);

        let card_entry = if *young_refs { CardEntry::Dirty } else { CardEntry:: Clean };
        self.card_table.set(card, card_entry);

        *young_refs = false;
    }

    fn is_marked(&self, obj: &Obj) -> bool {
        self.marking_bitmap.is_marked(Address::from_ptr(obj as *const _))
    }

    fn is_marked_addr(&self, addr: Address) -> bool {
        self.marking_bitmap.is_marked(addr)
    }

    fn mark(&mut self, addr: Address) {
        self.marking_bitmap.mark(addr);
    }
}

pub struct MarkingBitmap {
    heap: Region,
    bitmap: Region,
}

impl MarkingBitmap {
    pub fn new(heap: Region) -> MarkingBitmap {
        let bitmap_size = mem::page_align(heap.size() / mem::ptr_width_usize() / 8);
        let bitmap_start = Address::from_ptr(os::mmap(bitmap_size, os::Writable));

        if bitmap_start.is_null() {
            panic!("could not allocate marking bitmap.");
        }

        MarkingBitmap {
            heap: heap,
            bitmap: Region::new(bitmap_start, bitmap_start.offset(bitmap_size)),
        }
    }

    pub fn mark(&mut self, addr: Address) {
        let (byte_offset, bit_offset) = self.mark_offset(addr);

        let byte = self.mark_byte(byte_offset);
        let modified_byte = byte | (1 << bit_offset);
        self.set_mark_byte(byte_offset, modified_byte);
    }

    pub fn is_marked(&self, addr: Address) -> bool {
        let (byte_offset, bit_offset) = self.mark_offset(addr);
        let byte = self.mark_byte(byte_offset);

        byte & (1 << bit_offset) != 0
    }

    fn mark_offset(&self, addr: Address) -> (usize, u8) {
        debug_assert!(self.heap.contains(addr));
        let offset = addr.offset_from(self.heap.start) / mem::ptr_width_usize();
        let byte = offset / 8;
        let bit = offset % 8;

        (byte, bit as u8)
    }

    fn mark_byte(&self, offset: usize) -> u8 {
        let addr = self.bitmap.start.offset(offset);

        unsafe {
            *addr.to_ptr()
        }
    }

    fn set_mark_byte(&self, offset: usize, val: u8) {
        let addr = self.bitmap.start.offset(offset);

        unsafe {
            *addr.to_mut_ptr() = val;
        }
    }
}

impl Drop for MarkingBitmap {
    fn drop(&mut self) {
        os::munmap(self.bitmap.start.to_ptr(), self.bitmap.size());
    }
}

struct ForwardTable {
    data: HashMap<Address, AddressWithYoungRefs>,
}

impl ForwardTable {
    fn new() -> ForwardTable {
        ForwardTable {
            data: HashMap::new(),
        }
    }

    fn reserve_capacity(&mut self, count: usize) {
        self.data.reserve(count);
    }

    fn forward_to(&mut self, addr: Address, fwd: Address) {
        self.data.insert(addr, AddressWithYoungRefs::new(fwd));
    }

    fn forward_address(&mut self, addr: Address) -> Address {
        self.data.get(&addr).expect("no forward address found.").address()
    }

    fn has_young_refs(&mut self, addr: Address) -> bool {
        self.data.get(&addr).expect("no forward address found.").has_young_refs()
    }

    fn set_young_refs(&mut self, addr: Address) {
        let value = self.data.get_mut(&addr).expect("no forward address found.");
        *value = value.set_young_refs();
    }
}

#[derive(Copy, Clone)]
struct AddressWithYoungRefs {
    data: usize,
}

impl AddressWithYoungRefs {
    fn new(addr: Address) -> AddressWithYoungRefs {
        AddressWithYoungRefs {
            data: addr.to_usize()
        }
    }

    fn address(self) -> Address {
        (self.data & !1).into()
    }

    fn has_young_refs(self) -> bool {
        (self.data & 1) != 0
    }

    fn set_young_refs(self) -> AddressWithYoungRefs {
        AddressWithYoungRefs {
            data: self.data | 1
        }
    }
}<commit_msg>swiper: pass more data to update_corssing<commit_after>use std::collections::HashMap;
use std::sync::atomic::Ordering;

use ctxt::SemContext;
use gc::Address;
use gc::root::IndirectObj;
use gc::space::Space;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::CARD_SIZE;
use gc::swiper::crossing::CrossingMap;
use gc::swiper::{in_kilo, on_different_cards, start_of_card};
use gc::swiper::old::OldGen;
use gc::swiper::Region;
use gc::swiper::young::YoungGen;
use mem;
use object::Obj;
use os;
use timer::{in_ms, Timer};

pub struct FullCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    heap: Region,
    young: &'a YoungGen,
    old: &'a OldGen,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    perm_space: &'a Space,

    marking_bitmap: MarkingBitmap,
    fwd_table: ForwardTable,

    fwd: Address,
    fwd_end: Address,
    old_top: Address,
}

impl<'a, 'ast> FullCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        heap: Region,
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        perm_space: &'a Space,
        rootset: &'a [IndirectObj],
    ) -> FullCollector<'a, 'ast> {
        let marking_bitmap = MarkingBitmap::new(heap.clone());

        FullCollector {
            ctxt: ctxt,
            heap: heap,
            young: young,
            old: old,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            perm_space: perm_space,

            marking_bitmap: marking_bitmap,
            fwd_table: ForwardTable::new(),

            fwd: old.total.start,
            fwd_end: old.total.end,
            old_top: Address::null(),
        }
    }

    pub fn collect(&mut self) {
        let mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        let init_size = self.heap_size();

        self.mark_live();
        self.compute_forward();
        self.update_references();
        self.relocate();

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let garbage = init_size - new_size;
            let garbage_ratio = (garbage as f64 / init_size as f64) * 100f64;

            println!("GC: Full GC ({:.2} ms, {:.1}K->{:.1}K size, {:.1}K/{:.0}% garbage)",
                in_ms(dur), in_kilo(init_size), in_kilo(new_size),
                in_kilo(garbage), garbage_ratio);
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn mark_live(&mut self) {
        let mut marking_stack: Vec<Address> = Vec::new();
        let mut live_objects = 0;

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if self.heap.contains(root_ptr) {
                if !self.is_marked_addr(root_ptr) {
                    marking_stack.push(root_ptr);
                    self.mark(root_ptr);
                    live_objects += 1;
                }

            } else {
                debug_assert!(root_ptr.is_null() || self.perm_space.contains(root_ptr));
            }
        }

        while marking_stack.len() > 0 {
            let object_addr = marking_stack.pop().expect("stack already empty");
            let object = unsafe { &mut *object_addr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_addr = Address::from_ptr(field.get());

                if self.heap.contains(field_addr) {
                    if !self.is_marked_addr(field_addr) {
                        marking_stack.push(field_addr);
                        self.mark(field_addr);
                        live_objects += 1;
                    }

                } else {
                    debug_assert!(field_addr.is_null() || self.perm_space.contains(field_addr));
                }
            });
        }

        self.fwd_table.reserve_capacity(live_objects);
    }

    fn compute_forward(&mut self) {
        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let fwd = full.allocate(object_size);
                full.fwd_table.forward_to(address, fwd);
            }
        });
    }

    fn update_references(&mut self) {
        self.walk_old_and_young(|full, object, address, _| {
            if full.is_marked(object) {
                let mut young_refs = false;

                object.visit_reference_fields(|field| {
                    let field_addr = Address::from_ptr(field.get());

                    if !field_addr.is_null() && !full.perm_space.contains(field_addr) {
                        let fwd_addr = full.fwd_table.forward_address(field_addr);
                        field.set(fwd_addr.to_mut_ptr());

                        if full.young.contains(fwd_addr) {
                            young_refs = true;
                        }
                    }
                });

                if young_refs {
                    full.fwd_table.set_young_refs(address);
                }
            }
        });

        for root in self.rootset {
            let root_ptr = Address::from_ptr(root.get());

            if !root_ptr.is_null() && !self.perm_space.contains(root_ptr) {
                let fwd_addr = self.fwd_table.forward_address(root_ptr);
                root.set(fwd_addr.to_mut_ptr());
            }
        }
    }

    fn relocate(&mut self) {
        self.young.unprotect_to_space();

        self.crossing_map.set_first_object(0.into(), 0);
        let mut young_refs = false;
        let mut last_dest = Address::null();

        self.walk_old_and_young(|full, object, address, object_size| {
            if full.is_marked(object) {
                let dest = full.fwd_table.forward_address(address);
                object.copy_to(dest, object_size);

                if !young_refs {
                    young_refs = full.fwd_table.has_young_refs(address);
                }

                let next_dest = dest.offset(object_size);

                if on_different_cards(dest, next_dest) && full.old.contains(dest) {
                    full.update_crossing(dest, next_dest, object.is_array_ref());
                    full.update_card(dest, &mut young_refs);
                }

                last_dest = next_dest;
            }
        });

        if !last_dest.is_null() && !start_of_card(last_dest) && self.old.contains(last_dest) {
            self.update_card(last_dest, &mut young_refs);
        }

        let young_top;
        let old_top;

        // check if we have left the old-generation into
        // the young generation for copying objects
        if self.old_top.is_null() {
            // if not, young gen is empty
            young_top = self.young.to_space().start;
            old_top = self.fwd;

        } else {
            young_top = self.fwd;
            old_top = self.old_top;
        }

        debug_assert!(self.young.valid_top(young_top));
        self.young.swap_spaces(young_top);
        self.young.protect_to_space();

        debug_assert!(self.old.valid_top(old_top));
        self.old.free.store(old_top.to_usize(), Ordering::SeqCst);
    }

    fn walk_old_and_young<F>(&mut self, mut fct: F) where F: FnMut(&mut FullCollector, &mut Obj, Address, usize) {
        let used_region = self.old.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);

        let used_region = self.young.used_region();
        self.walk_region(used_region.start, used_region.end, &mut fct);
    }

    fn walk_region<F>(&mut self, start: Address, end: Address, fct: &mut F) where F: FnMut(&mut FullCollector, &mut Obj, Address, usize) {
        let mut scan = start;

        while scan < end {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };
            let object_size = object.size();

            fct(self, object, scan, object_size);

            scan = scan.offset(object_size);
        }
    }

    fn allocate(&mut self, object_size: usize) -> Address {
        let addr = self.fwd;
        let next = self.fwd.offset(object_size);

        if next <= self.fwd_end {
            self.fwd = next;
            return addr;
        }

        assert!(self.fwd_end == self.old.total.end);
        assert!(self.old_top.is_null());
        self.old_top = self.fwd;

        let young = self.young.to_space();
        self.fwd = young.start.offset(object_size);
        self.fwd_end = young.end;

        young.start
    }

    fn update_crossing(&mut self, _last: Address, addr: Address, _array_ref: bool) {
        let offset = addr.to_usize() & (CARD_SIZE - 1);
        let offset_words = offset / mem::ptr_width_usize();

        let card = self.old.card_from_address(addr);

        self.crossing_map.set_first_object(card, offset_words);
    }

    fn update_card(&mut self, addr: Address, young_refs: &mut bool) {
        let card = self.old.card_from_address(addr);

        let card_entry = if *young_refs { CardEntry::Dirty } else { CardEntry:: Clean };
        self.card_table.set(card, card_entry);

        *young_refs = false;
    }

    fn is_marked(&self, obj: &Obj) -> bool {
        self.marking_bitmap.is_marked(Address::from_ptr(obj as *const _))
    }

    fn is_marked_addr(&self, addr: Address) -> bool {
        self.marking_bitmap.is_marked(addr)
    }

    fn mark(&mut self, addr: Address) {
        self.marking_bitmap.mark(addr);
    }
}

pub struct MarkingBitmap {
    heap: Region,
    bitmap: Region,
}

impl MarkingBitmap {
    pub fn new(heap: Region) -> MarkingBitmap {
        let bitmap_size = mem::page_align(heap.size() / mem::ptr_width_usize() / 8);
        let bitmap_start = Address::from_ptr(os::mmap(bitmap_size, os::Writable));

        if bitmap_start.is_null() {
            panic!("could not allocate marking bitmap.");
        }

        MarkingBitmap {
            heap: heap,
            bitmap: Region::new(bitmap_start, bitmap_start.offset(bitmap_size)),
        }
    }

    pub fn mark(&mut self, addr: Address) {
        let (byte_offset, bit_offset) = self.mark_offset(addr);

        let byte = self.mark_byte(byte_offset);
        let modified_byte = byte | (1 << bit_offset);
        self.set_mark_byte(byte_offset, modified_byte);
    }

    pub fn is_marked(&self, addr: Address) -> bool {
        let (byte_offset, bit_offset) = self.mark_offset(addr);
        let byte = self.mark_byte(byte_offset);

        byte & (1 << bit_offset) != 0
    }

    fn mark_offset(&self, addr: Address) -> (usize, u8) {
        debug_assert!(self.heap.contains(addr));
        let offset = addr.offset_from(self.heap.start) / mem::ptr_width_usize();
        let byte = offset / 8;
        let bit = offset % 8;

        (byte, bit as u8)
    }

    fn mark_byte(&self, offset: usize) -> u8 {
        let addr = self.bitmap.start.offset(offset);

        unsafe {
            *addr.to_ptr()
        }
    }

    fn set_mark_byte(&self, offset: usize, val: u8) {
        let addr = self.bitmap.start.offset(offset);

        unsafe {
            *addr.to_mut_ptr() = val;
        }
    }
}

impl Drop for MarkingBitmap {
    fn drop(&mut self) {
        os::munmap(self.bitmap.start.to_ptr(), self.bitmap.size());
    }
}

struct ForwardTable {
    data: HashMap<Address, AddressWithYoungRefs>,
}

impl ForwardTable {
    fn new() -> ForwardTable {
        ForwardTable {
            data: HashMap::new(),
        }
    }

    fn reserve_capacity(&mut self, count: usize) {
        self.data.reserve(count);
    }

    fn forward_to(&mut self, addr: Address, fwd: Address) {
        self.data.insert(addr, AddressWithYoungRefs::new(fwd));
    }

    fn forward_address(&mut self, addr: Address) -> Address {
        self.data.get(&addr).expect("no forward address found.").address()
    }

    fn has_young_refs(&mut self, addr: Address) -> bool {
        self.data.get(&addr).expect("no forward address found.").has_young_refs()
    }

    fn set_young_refs(&mut self, addr: Address) {
        let value = self.data.get_mut(&addr).expect("no forward address found.");
        *value = value.set_young_refs();
    }
}

#[derive(Copy, Clone)]
struct AddressWithYoungRefs {
    data: usize,
}

impl AddressWithYoungRefs {
    fn new(addr: Address) -> AddressWithYoungRefs {
        AddressWithYoungRefs {
            data: addr.to_usize()
        }
    }

    fn address(self) -> Address {
        (self.data & !1).into()
    }

    fn has_young_refs(self) -> bool {
        (self.data & 1) != 0
    }

    fn set_young_refs(self) -> AddressWithYoungRefs {
        AddressWithYoungRefs {
            data: self.data | 1
        }
    }
}<|endoftext|>


<commit_before>pub struct Point {
    pub x: f32,
    pub y: f32
}

pub struct Rect {
    pub x: f32,
    pub y: f32,
    pub w: f32,
    pub h: f32,
}

impl Rect {
    pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
        Rect { x:x, y:y, w:w, h:h }
    }

    pub fn new_i32(x: i32, y: i32, w: i32, h: i32) -> Self {
        Rect { x:x as f32, y:y as f32, w:w as f32, h:h as f32 }
    }
}

pub struct Color(pub f32, pub f32, pub f32, pub f32);

impl Color {
    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
        Color(r, g, b, a)
    }
}

impl From<(u8, u8, u8, u8)> for Color {
    fn from(val: (u8, u8, u8, u8)) -> Self {
        let (r, g, b, a) = val;
        let rf = (r as f32) / 255.0;
        let gf = (g as f32) / 255.0;
        let bf = (b as f32) / 255.0;
        let af = (a as f32) / 255.0;
        Color(rf, gf, bf, af)
    }
}

pub enum BlendMode {
    Dummy
}
<commit_msg>Added color conversion functions and tests, just for fun.<commit_after>#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Point {
    pub x: f32,
    pub y: f32
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Rect {
    pub x: f32,
    pub y: f32,
    pub w: f32,
    pub h: f32,
}

impl Rect {
    pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
        Rect { x:x, y:y, w:w, h:h }
    }

    pub fn new_i32(x: i32, y: i32, w: i32, h: i32) -> Self {
        Rect { x:x as f32, y:y as f32, w:w as f32, h:h as f32 }
    }
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Color {
    pub r: f32,
    pub g: f32,
    pub b: f32,
    pub a: f32,
}

impl Color {
    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
        Color{r:r, g:g, b:b, a:a}
    }
}

impl From<(u8, u8, u8, u8)> for Color {
    fn from(val: (u8, u8, u8, u8)) -> Self {
        let (r, g, b, a) = val;
        let rf = (r as f32) / 255.0;
        let gf = (g as f32) / 255.0;
        let bf = (b as f32) / 255.0;
        let af = (a as f32) / 255.0;
        Color::new(rf, gf, bf, af)
    }
}

impl From<(u8, u8, u8)> for Color {
    fn from(val: (u8, u8, u8)) -> Self {
        let (r, g, b) = val;
        Color::from((r, g, b, 255))
    }
}

impl From<Color> for (u8, u8, u8, u8) {
    fn from(color: Color) -> Self {
        let r = (color.r * 255.0) as u8;
        let g = (color.g * 255.0) as u8;
        let b = (color.b * 255.0) as u8;
        let a = (color.a * 255.0) as u8;
        (r, g, b, a)
    }
}

impl From<Color> for (u8, u8, u8) {
    fn from(color: Color) -> Self {
        let (r, g, b, _) = color.into();
        (r, g, b)
    }
}

impl From<Color> for u32 {
    fn from(color: Color) -> Self {
        let (r, g, b, a): (u8, u8, u8, u8) = color.into();
        let rp = (r as u32) << 24;
        let gp = (g as u32) << 16;
        let bp = (b as u32) << 8;
        let ap = a as u32;
        (rp | gp | bp | ap)
    }
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum BlendMode {
    Dummy
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_color_conversions() {
        let white = Color::new(1.0, 1.0, 1.0, 1.0);
        let w1 = Color::from((255, 255, 255, 255));
        assert_eq!(white, w1);
        let w2: u32 = white.into();
        assert_eq!(w2, 0xFFFFFFFF);

        let grey = Color::new(0.5019608, 0.5019608, 0.5019608, 1.0);
        let g1 = Color::from((128, 128, 128, 255));
        assert_eq!(grey, g1);
        let g2: u32 = grey.into();
        assert_eq!(g2, 0x808080FF);

        let black = Color::new(0.0, 0.0, 0.0, 1.0);
        let b1 = Color::from((0, 0, 0, 255));
        assert_eq!(black, b1);
        let b2: u32 = black.into();
        assert_eq!(b2, 0x000000FF);
    }
}
<|endoftext|>