<commit_before>///! Basic CSS block layout.

use style::{StyledNode, Inline, Block, DisplayNone};
use css::{Keyword, Length, Px};
use std::default::Default;
use std::iter::AdditiveIterator; // for `sum`

// CSS box model. All sizes are in px.

#[deriving(Default, Show)]
pub struct Rect {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
}

#[deriving(Default, Show)]
pub struct Dimensions {
    /// Position of the content area relative to the document origin:
    pub content: Rect,
    // Surrounding edges:
    pub padding: EdgeSizes,
    pub border: EdgeSizes,
    pub margin: EdgeSizes,
}

#[deriving(Default, Show)]
pub struct EdgeSizes {
    pub left: f32,
    pub right: f32,
    pub top: f32,
    pub bottom: f32,
}

/// A node in the layout tree.
pub struct LayoutBox<'a> {
    pub dimensions: Dimensions,
    pub box_type: BoxType<'a>,
    pub children: Vec<LayoutBox<'a>>,
}

pub enum BoxType<'a> {
    BlockNode(&'a StyledNode<'a>),
    InlineNode(&'a StyledNode<'a>),
    AnonymousBlock,
}

impl<'a> LayoutBox<'a> {
    fn new(box_type: BoxType) -> LayoutBox {
        LayoutBox {
            box_type: box_type,
            dimensions: Default::default(),
            children: Vec::new(),
        }
    }

    fn get_style_node(&self) -> &'a StyledNode<'a> {
        match self.box_type {
            BlockNode(node) => node,
            InlineNode(node) => node,
            AnonymousBlock => panic!("Anonymous block box has no style node")
        }
    }
}

/// Transform a style tree into a layout tree.
pub fn layout_tree<'a>(node: &'a StyledNode<'a>, containing_block: Dimensions) -> LayoutBox<'a> {
    let mut root_box = build_layout_tree(node);
    root_box.layout(containing_block);
    return root_box;
}

/// Build the tree of LayoutBoxes, but don't perform any layout calculations yet.
fn build_layout_tree<'a>(style_node: &'a StyledNode<'a>) -> LayoutBox<'a> {
    // Create the root box.
    let mut root = LayoutBox::new(match style_node.display() {
        Block => BlockNode(style_node),
        Inline => InlineNode(style_node),
        DisplayNone => panic!("Root node has display: none.")
    });

    // Create the descendant boxes.
    for child in style_node.children.iter() {
        match child.display() {
            Block => root.children.push(build_layout_tree(child)),
            Inline => root.get_inline_container().children.push(build_layout_tree(child)),
            DisplayNone => {} // Don't lay out nodes with `display: none;`
        }
    }
    return root;
}

impl<'a> LayoutBox<'a> {
    /// Lay out a box and its descendants.
    fn layout(&mut self, containing_block: Dimensions) {
        match self.box_type {
            BlockNode(_) => self.layout_block(containing_block),
            InlineNode(_) => {} // TODO
            AnonymousBlock => {} // TODO
        }
    }

    /// Lay out a block-level element and its descendants.
    fn layout_block(&mut self, containing_block: Dimensions) {
        // Child width can depend on parent width, so we need to calculate this box's width before
        // laying out its children.
        self.calculate_block_width(containing_block);

        // Determine where the box is located within its container.
        self.calculate_block_position(containing_block);

        // Recursively lay out the children of this box.
        self.layout_block_children();

        // Parent height can depend on child height, so `calculate_height` must be called after the
        // children are laid out.
        self.calculate_block_height();
    }

    /// Calculate the width of a block-level non-replaced element in normal flow.
    ///
    /// http://www.w3.org/TR/CSS2/visudet.html#blockwidth
    ///
    /// Sets the horizontal margin/padding/border dimensions, and the `width`.
    fn calculate_block_width(&mut self, containing_block: Dimensions) {
        let style = self.get_style_node();

        // `width` has initial value `auto`.
        let auto = Keyword("auto".to_string());
        let mut width = style.value("width").unwrap_or(auto.clone());

        // margin, border, and padding have initial value 0.
        let zero = Length(0.0, Px);

        let mut margin_left = style.lookup("margin-left", "margin", &zero);
        let mut margin_right = style.lookup("margin-right", "margin", &zero);

        let border_left = style.lookup("border-left-width", "border-width", &zero);
        let border_right = style.lookup("border-right-width", "border-width", &zero);

        let padding_left = style.lookup("padding-left", "padding", &zero);
        let padding_right = style.lookup("padding-right", "padding", &zero);

        let total = [&margin_left, &margin_right, &border_left, &border_right,
                     &padding_left, &padding_right, &width].iter().map(|v| v.to_px()).sum();

        // If width is not auto and the total is wider than the container, treat auto margins as 0.
        if width != auto && total > containing_block.content.width {
            if margin_left == auto {
                margin_left = Length(0.0, Px);
            }
            if margin_right == auto {
                margin_right = Length(0.0, Px);
            }
        }

        // Adjust used values so that the above sum equals `containing_block.width`.
        // Each arm of the `match` should increase the total width by exactly `underflow`,
        // and afterward all values should be absolute lengths in px.
        let underflow = containing_block.content.width - total;

        match (width == auto, margin_left == auto, margin_right == auto) {
            // If the values are overconstrained, calculate margin_right.
            (false, false, false) => {
                margin_right = Length(margin_right.to_px() + underflow, Px);
            }

            // If exactly one size is auto, its used value follows from the equality.
            (false, false, true) => { margin_right = Length(underflow, Px); }
            (false, true, false) => { margin_left  = Length(underflow, Px); }

            // If width is set to auto, any other auto values become 0.
            (true, _, _) => {
                if margin_left == auto { margin_left = Length(0.0, Px); }
                if margin_right == auto { margin_right = Length(0.0, Px); }

                if underflow >= 0.0 {
                    // Expand width to fill the underflow.
                    width = Length(underflow, Px);
                } else {
                    // Width can't be negative. Adjust the right margin instead.
                    width = Length(0.0, Px);
                    margin_right = Length(margin_right.to_px() + underflow, Px);
                }
            }

            // If margin-left and margin-right are both auto, their used values are equal.
            (false, true, true) => {
                margin_left = Length(underflow / 2.0, Px);
                margin_right = Length(underflow / 2.0, Px);
            }
        }

        let d = &mut self.dimensions;
        d.content.width = width.to_px();

        d.padding.left = padding_left.to_px();
        d.padding.right = padding_right.to_px();

        d.border.left = border_left.to_px();
        d.border.right = border_right.to_px();

        d.margin.left = margin_left.to_px();
        d.margin.right = margin_right.to_px();
    }

    /// Finish calculating the block's edge sizes, and position it within its containing block.
    ///
    /// http://www.w3.org/TR/CSS2/visudet.html#normal-block
    ///
    /// Sets the vertical margin/padding/border dimensions, and the `x`, `y` values.
    fn calculate_block_position(&mut self, containing_block: Dimensions) {
        let style = self.get_style_node();
        let d = &mut self.dimensions;

        // margin, border, and padding have initial value 0.
        let zero = Length(0.0, Px);

        // If margin-top or margin-bottom is `auto`, the used value is zero.
        d.margin.top = style.lookup("margin-top", "margin", &zero).to_px();
        d.margin.bottom = style.lookup("margin-bottom", "margin", &zero).to_px();

        d.border.top = style.lookup("border-top-width", "border-width", &zero).to_px();
        d.border.bottom = style.lookup("border-bottom-width", "border-width", &zero).to_px();

        d.padding.top = style.lookup("padding-top", "padding", &zero).to_px();
        d.padding.bottom = style.lookup("padding-bottom", "padding", &zero).to_px();

        // Position the box below all the previous boxes in the container.
        d.content.x = containing_block.content.x +
                      d.margin.left + d.border.left + d.padding.left;
        d.content.y = containing_block.content.y + containing_block.content.height +
                      d.margin.top + d.border.top + d.padding.top;
    }

    /// Lay out the block's children within its content area.
    ///
    /// Sets `self.dimensions.height` to the total content height.
    fn layout_block_children(&mut self) {
        let d = &mut self.dimensions;
        for child in self.children.iter_mut() {
            child.layout(*d);
            // Increment the height so each child is laid out below the previous one.
            d.content.height = d.content.height + child.dimensions.margin_box_height();
        }
    }

    /// Height of a block-level non-replaced element in normal flow with overflow visible.
    fn calculate_block_height(&mut self) {
        // If the height is set to an explicit length, use that exact length.
        // Otherwise, just keep the value set by `layout_block_children`.
        match self.get_style_node().value("height") {
            Some(Length(h, Px)) => { self.dimensions.content.height = h; }
            _ => {}
        }
    }

    /// Where a new inline child should go.
    fn get_inline_container(&mut self) -> &mut LayoutBox<'a> {
        match self.box_type {
            InlineNode(_) | AnonymousBlock => self,
            BlockNode(_) => {
                // If we've just generated an anonymous block box, keep using it.
                // Otherwise, create a new one.
                match self.children.last() {
                    Some(&LayoutBox { box_type: AnonymousBlock,..}) => {}
                    _ => self.children.push(LayoutBox::new(AnonymousBlock))
                }
                self.children.last_mut().unwrap()
            }
        }
    }
}

impl Dimensions {
    /// Total height of a box including its margins, border, and padding.
    fn margin_box_height(&self) -> f32 {
        self.content.height + self.padding.top + self.padding.bottom
                            + self.border.top + self.border.bottom
                            + self.margin.top + self.margin.bottom
    }

    pub fn padding_box(&self) -> Rect {
        Rect {
            x: self.content.x - self.padding.left,
            y: self.content.y - self.padding.top,
            width: self.content.width + self.padding.left + self.padding.right,
            height: self.content.height + self.padding.top + self.padding.bottom,
        }
    }
}
<commit_msg>Bug fix: start layout at the top if the viewport<commit_after>///! Basic CSS block layout.

use style::{StyledNode, Inline, Block, DisplayNone};
use css::{Keyword, Length, Px};
use std::default::Default;
use std::iter::AdditiveIterator; // for `sum`

// CSS box model. All sizes are in px.

#[deriving(Default, Show)]
pub struct Rect {
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
}

#[deriving(Default, Show)]
pub struct Dimensions {
    /// Position of the content area relative to the document origin:
    pub content: Rect,
    // Surrounding edges:
    pub padding: EdgeSizes,
    pub border: EdgeSizes,
    pub margin: EdgeSizes,
}

#[deriving(Default, Show)]
pub struct EdgeSizes {
    pub left: f32,
    pub right: f32,
    pub top: f32,
    pub bottom: f32,
}

/// A node in the layout tree.
pub struct LayoutBox<'a> {
    pub dimensions: Dimensions,
    pub box_type: BoxType<'a>,
    pub children: Vec<LayoutBox<'a>>,
}

pub enum BoxType<'a> {
    BlockNode(&'a StyledNode<'a>),
    InlineNode(&'a StyledNode<'a>),
    AnonymousBlock,
}

impl<'a> LayoutBox<'a> {
    fn new(box_type: BoxType) -> LayoutBox {
        LayoutBox {
            box_type: box_type,
            dimensions: Default::default(),
            children: Vec::new(),
        }
    }

    fn get_style_node(&self) -> &'a StyledNode<'a> {
        match self.box_type {
            BlockNode(node) => node,
            InlineNode(node) => node,
            AnonymousBlock => panic!("Anonymous block box has no style node")
        }
    }
}

/// Transform a style tree into a layout tree.
pub fn layout_tree<'a>(node: &'a StyledNode<'a>, mut containing_block: Dimensions) -> LayoutBox<'a> {
    // The layout algorithm expects the container height to start at 0.
    // TODO: Save the initial containing block height, for calculating percent heights.
    containing_block.content.height = 0.0;

    let mut root_box = build_layout_tree(node);
    root_box.layout(containing_block);
    return root_box;
}

/// Build the tree of LayoutBoxes, but don't perform any layout calculations yet.
fn build_layout_tree<'a>(style_node: &'a StyledNode<'a>) -> LayoutBox<'a> {
    // Create the root box.
    let mut root = LayoutBox::new(match style_node.display() {
        Block => BlockNode(style_node),
        Inline => InlineNode(style_node),
        DisplayNone => panic!("Root node has display: none.")
    });

    // Create the descendant boxes.
    for child in style_node.children.iter() {
        match child.display() {
            Block => root.children.push(build_layout_tree(child)),
            Inline => root.get_inline_container().children.push(build_layout_tree(child)),
            DisplayNone => {} // Don't lay out nodes with `display: none;`
        }
    }
    return root;
}

impl<'a> LayoutBox<'a> {
    /// Lay out a box and its descendants.
    fn layout(&mut self, containing_block: Dimensions) {
        match self.box_type {
            BlockNode(_) => self.layout_block(containing_block),
            InlineNode(_) => {} // TODO
            AnonymousBlock => {} // TODO
        }
    }

    /// Lay out a block-level element and its descendants.
    fn layout_block(&mut self, containing_block: Dimensions) {
        // Child width can depend on parent width, so we need to calculate this box's width before
        // laying out its children.
        self.calculate_block_width(containing_block);

        // Determine where the box is located within its container.
        self.calculate_block_position(containing_block);

        // Recursively lay out the children of this box.
        self.layout_block_children();

        // Parent height can depend on child height, so `calculate_height` must be called after the
        // children are laid out.
        self.calculate_block_height();
    }

    /// Calculate the width of a block-level non-replaced element in normal flow.
    ///
    /// http://www.w3.org/TR/CSS2/visudet.html#blockwidth
    ///
    /// Sets the horizontal margin/padding/border dimensions, and the `width`.
    fn calculate_block_width(&mut self, containing_block: Dimensions) {
        let style = self.get_style_node();

        // `width` has initial value `auto`.
        let auto = Keyword("auto".to_string());
        let mut width = style.value("width").unwrap_or(auto.clone());

        // margin, border, and padding have initial value 0.
        let zero = Length(0.0, Px);

        let mut margin_left = style.lookup("margin-left", "margin", &zero);
        let mut margin_right = style.lookup("margin-right", "margin", &zero);

        let border_left = style.lookup("border-left-width", "border-width", &zero);
        let border_right = style.lookup("border-right-width", "border-width", &zero);

        let padding_left = style.lookup("padding-left", "padding", &zero);
        let padding_right = style.lookup("padding-right", "padding", &zero);

        let total = [&margin_left, &margin_right, &border_left, &border_right,
                     &padding_left, &padding_right, &width].iter().map(|v| v.to_px()).sum();

        // If width is not auto and the total is wider than the container, treat auto margins as 0.
        if width != auto && total > containing_block.content.width {
            if margin_left == auto {
                margin_left = Length(0.0, Px);
            }
            if margin_right == auto {
                margin_right = Length(0.0, Px);
            }
        }

        // Adjust used values so that the above sum equals `containing_block.width`.
        // Each arm of the `match` should increase the total width by exactly `underflow`,
        // and afterward all values should be absolute lengths in px.
        let underflow = containing_block.content.width - total;

        match (width == auto, margin_left == auto, margin_right == auto) {
            // If the values are overconstrained, calculate margin_right.
            (false, false, false) => {
                margin_right = Length(margin_right.to_px() + underflow, Px);
            }

            // If exactly one size is auto, its used value follows from the equality.
            (false, false, true) => { margin_right = Length(underflow, Px); }
            (false, true, false) => { margin_left  = Length(underflow, Px); }

            // If width is set to auto, any other auto values become 0.
            (true, _, _) => {
                if margin_left == auto { margin_left = Length(0.0, Px); }
                if margin_right == auto { margin_right = Length(0.0, Px); }

                if underflow >= 0.0 {
                    // Expand width to fill the underflow.
                    width = Length(underflow, Px);
                } else {
                    // Width can't be negative. Adjust the right margin instead.
                    width = Length(0.0, Px);
                    margin_right = Length(margin_right.to_px() + underflow, Px);
                }
            }

            // If margin-left and margin-right are both auto, their used values are equal.
            (false, true, true) => {
                margin_left = Length(underflow / 2.0, Px);
                margin_right = Length(underflow / 2.0, Px);
            }
        }

        let d = &mut self.dimensions;
        d.content.width = width.to_px();

        d.padding.left = padding_left.to_px();
        d.padding.right = padding_right.to_px();

        d.border.left = border_left.to_px();
        d.border.right = border_right.to_px();

        d.margin.left = margin_left.to_px();
        d.margin.right = margin_right.to_px();
    }

    /// Finish calculating the block's edge sizes, and position it within its containing block.
    ///
    /// http://www.w3.org/TR/CSS2/visudet.html#normal-block
    ///
    /// Sets the vertical margin/padding/border dimensions, and the `x`, `y` values.
    fn calculate_block_position(&mut self, containing_block: Dimensions) {
        let style = self.get_style_node();
        let d = &mut self.dimensions;

        // margin, border, and padding have initial value 0.
        let zero = Length(0.0, Px);

        // If margin-top or margin-bottom is `auto`, the used value is zero.
        d.margin.top = style.lookup("margin-top", "margin", &zero).to_px();
        d.margin.bottom = style.lookup("margin-bottom", "margin", &zero).to_px();

        d.border.top = style.lookup("border-top-width", "border-width", &zero).to_px();
        d.border.bottom = style.lookup("border-bottom-width", "border-width", &zero).to_px();

        d.padding.top = style.lookup("padding-top", "padding", &zero).to_px();
        d.padding.bottom = style.lookup("padding-bottom", "padding", &zero).to_px();

        // Position the box below all the previous boxes in the container.
        d.content.x = containing_block.content.x +
                      d.margin.left + d.border.left + d.padding.left;
        d.content.y = containing_block.content.y + containing_block.content.height +
                      d.margin.top + d.border.top + d.padding.top;
    }

    /// Lay out the block's children within its content area.
    ///
    /// Sets `self.dimensions.height` to the total content height.
    fn layout_block_children(&mut self) {
        let d = &mut self.dimensions;
        for child in self.children.iter_mut() {
            child.layout(*d);
            // Increment the height so each child is laid out below the previous one.
            d.content.height = d.content.height + child.dimensions.margin_box_height();
        }
    }

    /// Height of a block-level non-replaced element in normal flow with overflow visible.
    fn calculate_block_height(&mut self) {
        // If the height is set to an explicit length, use that exact length.
        // Otherwise, just keep the value set by `layout_block_children`.
        match self.get_style_node().value("height") {
            Some(Length(h, Px)) => { self.dimensions.content.height = h; }
            _ => {}
        }
    }

    /// Where a new inline child should go.
    fn get_inline_container(&mut self) -> &mut LayoutBox<'a> {
        match self.box_type {
            InlineNode(_) | AnonymousBlock => self,
            BlockNode(_) => {
                // If we've just generated an anonymous block box, keep using it.
                // Otherwise, create a new one.
                match self.children.last() {
                    Some(&LayoutBox { box_type: AnonymousBlock,..}) => {}
                    _ => self.children.push(LayoutBox::new(AnonymousBlock))
                }
                self.children.last_mut().unwrap()
            }
        }
    }
}

impl Dimensions {
    /// Total height of a box including its margins, border, and padding.
    fn margin_box_height(&self) -> f32 {
        self.content.height + self.padding.top + self.padding.bottom
                            + self.border.top + self.border.bottom
                            + self.margin.top + self.margin.bottom
    }

    pub fn padding_box(&self) -> Rect {
        Rect {
            x: self.content.x - self.padding.left,
            y: self.content.y - self.padding.top,
            width: self.content.width + self.padding.left + self.padding.right,
            height: self.content.height + self.padding.top + self.padding.bottom,
        }
    }
}
<|endoftext|>


<commit_before>/// TODO: parse and return flags per DSO, add as entry to the struct
/// TODO: fix the high address mapperr for `__libc_start_main`

use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::os::raw::{c_int};

use utils::{self, mmap, page};
use image::SharedObject;
use binary::elf::header;
use binary::elf::program_header;
use binary::elf::dyn;
use binary::elf::sym;
use binary::elf::rela;
use binary::elf::strtab::Strtab;
use binary::elf::gnu_hash::GnuHash;

#[inline(always)]
fn compute_load_size (phdrs: &[program_header::ProgramHeader]) -> (usize, u64, u64) {
    let mut max_vaddr = 0;
    let mut min_vaddr = 0;
    for phdr in phdrs {

        if phdr.p_type != program_header::PT_LOAD {
            continue
        }

        if phdr.p_vaddr < min_vaddr {
            min_vaddr = phdr.p_vaddr;
        }

        if phdr.p_vaddr + phdr.p_memsz > max_vaddr {
            max_vaddr = phdr.p_vaddr + phdr.p_memsz;
        }
    }

    min_vaddr = page::page_start(min_vaddr);
    max_vaddr = page::page_end(max_vaddr);

    ((max_vaddr - min_vaddr) as usize, min_vaddr, max_vaddr)
}

#[inline(always)]
fn reserve_address_space (phdrs: &[program_header::ProgramHeader]) -> Result <(u64, u64, u64), String> {

    let (size, min_vaddr, max_vaddr) = compute_load_size(&phdrs);

    let mmap_flags = mmap::MAP_PRIVATE | mmap::MAP_ANONYMOUS;
    let start = unsafe { mmap::mmap(0 as *const u64,
                                    size,
                                    // for now, using PROT_NONE seems to give SEGV_ACCERR on execution of PT_LOAD mmaped segments (i.e., operation not allowed on mapped object)
                                    mmap::PROT_EXEC | mmap::PROT_READ | mmap::PROT_WRITE,
                                    mmap_flags as c_int,  // TODO: I think we should copy glibc's lead here and use their mmap flags
                                    -1,
                                    0) };

    if start == mmap::MAP_FAILED {

        Err (format!("Error: anonymous mmap failed for size {:x} with errno {}", size, utils::get_errno()))

    } else {

        let load_bias = start - min_vaddr;
        let end = start + size as u64;
        Ok ((start, load_bias, end))
    }
}

#[inline(always)]
fn pflags_to_prot (x: u32) -> isize {
    use binary::elf::program_header::{PF_X, PF_R, PF_W};

    // I'm a dick for writing this/copying maniac C programmer implementations: but it checks the flag to see if it's the PF value,
    // and returns the appropriate mmap version, and logical ORs this for use in the mmap prot argument
    (if x & PF_X == PF_X { mmap::PROT_EXEC } else { 0 }) |
    (if x & PF_R == PF_R { mmap::PROT_READ } else { 0 }) |
    (if x & PF_W == PF_W { mmap::PROT_WRITE } else { 0 })
}

/// Loads an ELF binary from the given fd, mmaps its contents, and returns a SharedObject, whose lifetime is tied to the mmap's, i.e., manually managed
pub fn load<'a> (soname: &str, load_path: String, fd: &mut File, debug: bool) -> Result <SharedObject<'a>, String> {

    ///////////////
    // Part I:
    //   wherein we read the binary from disk,
    //   and lovingly mmap it's joyous contents
    ///////////////

    // 1. Suck up the elf header on disk and construct the program headers
    let ehdr = header::Header::from_fd(fd).map_err(|e| format!("Error {:?}", e))?;
    let phdrs = program_header::ProgramHeader::from_fd(fd, ehdr.e_phoff, ehdr.e_phnum as usize).map_err(|e| format!("Error {:?}", e))?;

    // 2. Reserve address space with anon mmap
    let (start, load_bias, end) = reserve_address_space(&phdrs)?;
    dbgc!(red_bold: debug, "loader", "reserved {:#x} - {:#x} with load_bias: 0x{:x}", start, end, load_bias);

    // 3. Now we iterate through the phdrs, and
    // a. mmap the PT_LOAD program headers
    // b. collect the vaddrs of the phdrs and the dynamic array
    // c. TODO: mmap and setup TLS
    let mut phdrs_vaddr = 0;
    let mut dynamic_vaddr = None;
    let mut has_pt_load = false;
    for phdr in &phdrs {

        match phdr.p_type {

            program_header::PT_LOAD => {
                has_pt_load = true;
                // Segment offsets: rounds down the segment start to a value suitable for mmaping, and adjusts the size of the
                // mmap breadth appropriately
                let seg_start: u64 = phdr.p_vaddr + load_bias;
                let seg_end: u64   = seg_start + phdr.p_memsz;

                let seg_page_start: u64 = page::page_start(seg_start);
                let seg_page_end: u64   = page::page_start(seg_end);

                // TODO: unused, I think we need to zero some stuff
                let seg_file_end: u64 = seg_start + phdr.p_filesz;

                // File offsets.
                let file_start: u64 = phdr.p_offset;
                let file_end: u64   = file_start + phdr.p_filesz;

                // "rounds" to an mmap-able value (i.e., file_start % pagesize)
                // file_page_start <= file_start
                // so sometimes the beginning of the page is not the beginning of the PT_LOAD!
                let file_page_start = page::page_start(file_start);
                let file_length: u64 = file_end - file_page_start;

                // TODO: add error checking, if file size <= 0, if file_end greater than file_size, etc.

                dbgc!(red_bold: debug, "loader", "PT_LOAD:\n\tseg_start: {:x} seg_end: {:x} seg_page_start: {:x} seg_page_end: {:x} seg_file_end: {:x}\n\tfile_start: {:x} file_end: {:x} file_page_start: {:x} file_length: {:x}", seg_start, seg_end, seg_page_start, seg_page_end, seg_file_end, file_start, file_end, file_page_start, file_length);

                if file_length != 0 {
                    let mmap_flags = mmap::MAP_FIXED | mmap::MAP_PRIVATE;
                    let prot_flags = pflags_to_prot(phdr.p_flags);
                    unsafe {
                        let start = mmap::mmap(seg_page_start as *const u64,
                                               file_length as usize,
                                               prot_flags,
                                               mmap_flags as c_int,
                                               fd.as_raw_fd() as c_int,
                                               file_page_start as usize);

                        if start == mmap::MAP_FAILED {

                            return Err(format!("loader loading phdrs for {} failed with errno {}, aborting execution", &soname, utils::get_errno()))
                        }
                    }

                    // TODO: other more boring shit to do with zero'd out extra pages if too big, etc.
                    //seg_file_end = page::page_end(seg_file_end);

                }
            } // end match PT_LOAD

            program_header::PT_PHDR => {
                phdrs_vaddr = phdr.p_vaddr;
            },
            program_header::PT_DYNAMIC => {
                dynamic_vaddr = Some(phdr.p_vaddr);
            },
            _ => () // do nothing, i.e., continue
        }
    }

    if !has_pt_load {
        return Err(format!("loader {} has no PT_LOAD sections", soname));
    }

    ///////////////
    // Part Deux:
    //   wherein we construct our components for this SharedObject
    //   from the newly mmap'd memory
    ///////////////

    // use the now mmap'd program headers
    let phdrs = unsafe { program_header::ProgramHeader::from_raw_parts((phdrs_vaddr + load_bias) as *const program_header::ProgramHeader, phdrs.len()) };

    // construct the dynamic slice in whatever mmap'd PT_LOAD it's in
    let dynamic_vaddr = dynamic_vaddr.ok_or(format!("loader {} has no dynamic array", soname))?;
    let dynamic = unsafe { dyn::from_raw(load_bias, dynamic_vaddr) };

    // build the link info with the bias and the dynamic array
    let link_info = dyn::LinkInfo::new(&dynamic, load_bias as usize);

    // now get the strtab from the dynamic array
    let strtab = unsafe { Strtab::from_raw(link_info.strtab as *const u8, link_info.strsz as usize) };

    // now get the libs we will need
    let libs = dyn::get_needed(dynamic, &strtab, link_info.needed_count);

    // caveat about rdr doing this for hundres of binaries and it being "ok"
    let num_syms = (link_info.strtab - link_info.symtab) / sym::SIZEOF_SYM;

    // now construct the symtab
    let symtab = unsafe { sym::from_raw(link_info.symtab as *const sym::Sym, num_syms) };

    // now grab relatab, and pltreltab which we'll use for relocating this shared object later
    let relatab = unsafe { rela::from_raw(link_info.rela as *const rela::Rela, link_info.relasz) };
    let pltrelatab = unsafe { rela::from_raw(link_info.jmprel as *const rela::Rela, link_info.pltrelsz) };

    // the pltgot we need for doing lazy dynamic linking
    let pltgot = if let Some(addr) = link_info.pltgot { addr } else { 0 }; // musl doesn't have a PLTGOT, for example

    // and finally grab the gnu_hash (if it has one)
    let gnu_hash = if let Some(addr) = link_info.gnu_hash { Some (GnuHash::new(addr as *const u32, symtab.len())) } else { None };

    let shared_object = SharedObject {
        name: strtab.get(link_info.soname),
        load_bias: load_bias,
        libs: libs,
        map_begin: start,
        map_end: end,
        phdrs: phdrs,
        dynamic: dynamic,
        symtab: symtab,
        strtab: strtab,
        relatab: relatab,
        pltrelatab: pltrelatab,
        pltgot: pltgot as *const u64,
        gnu_hash: gnu_hash,
        load_path: Some (load_path),
        flags: link_info.flags,
        state_flags: link_info.flags_1,
    };

    Ok (shared_object)
}
<commit_msg>loader: add Error prefix to error messages<commit_after>/// TODO: parse and return flags per DSO, add as entry to the struct
/// TODO: fix the high address mapperr for `__libc_start_main`

use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::os::raw::{c_int};

use utils::{self, mmap, page};
use image::SharedObject;
use binary::elf::header;
use binary::elf::program_header;
use binary::elf::dyn;
use binary::elf::sym;
use binary::elf::rela;
use binary::elf::strtab::Strtab;
use binary::elf::gnu_hash::GnuHash;

#[inline(always)]
fn compute_load_size (phdrs: &[program_header::ProgramHeader]) -> (usize, u64, u64) {
    let mut max_vaddr = 0;
    let mut min_vaddr = 0;
    for phdr in phdrs {

        if phdr.p_type != program_header::PT_LOAD {
            continue
        }

        if phdr.p_vaddr < min_vaddr {
            min_vaddr = phdr.p_vaddr;
        }

        if phdr.p_vaddr + phdr.p_memsz > max_vaddr {
            max_vaddr = phdr.p_vaddr + phdr.p_memsz;
        }
    }

    min_vaddr = page::page_start(min_vaddr);
    max_vaddr = page::page_end(max_vaddr);

    ((max_vaddr - min_vaddr) as usize, min_vaddr, max_vaddr)
}

#[inline(always)]
fn reserve_address_space (phdrs: &[program_header::ProgramHeader]) -> Result <(u64, u64, u64), String> {

    let (size, min_vaddr, max_vaddr) = compute_load_size(&phdrs);

    let mmap_flags = mmap::MAP_PRIVATE | mmap::MAP_ANONYMOUS;
    let start = unsafe { mmap::mmap(0 as *const u64,
                                    size,
                                    // for now, using PROT_NONE seems to give SEGV_ACCERR on execution of PT_LOAD mmaped segments (i.e., operation not allowed on mapped object)
                                    mmap::PROT_EXEC | mmap::PROT_READ | mmap::PROT_WRITE,
                                    mmap_flags as c_int,  // TODO: I think we should copy glibc's lead here and use their mmap flags
                                    -1,
                                    0) };

    if start == mmap::MAP_FAILED {

        Err (format!("Error: anonymous mmap failed for size {:x} with errno {}", size, utils::get_errno()))

    } else {

        let load_bias = start - min_vaddr;
        let end = start + size as u64;
        Ok ((start, load_bias, end))
    }
}

#[inline(always)]
fn pflags_to_prot (x: u32) -> isize {
    use binary::elf::program_header::{PF_X, PF_R, PF_W};

    // I'm a dick for writing this/copying maniac C programmer implementations: but it checks the flag to see if it's the PF value,
    // and returns the appropriate mmap version, and logical ORs this for use in the mmap prot argument
    (if x & PF_X == PF_X { mmap::PROT_EXEC } else { 0 }) |
    (if x & PF_R == PF_R { mmap::PROT_READ } else { 0 }) |
    (if x & PF_W == PF_W { mmap::PROT_WRITE } else { 0 })
}

/// Loads an ELF binary from the given fd, mmaps its contents, and returns a SharedObject, whose lifetime is tied to the mmap's, i.e., manually managed
pub fn load<'a> (soname: &str, load_path: String, fd: &mut File, debug: bool) -> Result <SharedObject<'a>, String> {

    ///////////////
    // Part I:
    //   wherein we read the binary from disk,
    //   and lovingly mmap it's joyous contents
    ///////////////

    // 1. Suck up the elf header on disk and construct the program headers
    let ehdr = header::Header::from_fd(fd).map_err(|e| format!("Error {:?}", e))?;
    let phdrs = program_header::ProgramHeader::from_fd(fd, ehdr.e_phoff, ehdr.e_phnum as usize).map_err(|e| format!("Error {:?}", e))?;

    // 2. Reserve address space with anon mmap
    let (start, load_bias, end) = reserve_address_space(&phdrs)?;
    dbgc!(red_bold: debug, "loader", "reserved {:#x} - {:#x} with load_bias: 0x{:x}", start, end, load_bias);

    // 3. Now we iterate through the phdrs, and
    // a. mmap the PT_LOAD program headers
    // b. collect the vaddrs of the phdrs and the dynamic array
    // c. TODO: mmap and setup TLS
    let mut phdrs_vaddr = 0;
    let mut dynamic_vaddr = None;
    let mut has_pt_load = false;
    for phdr in &phdrs {

        match phdr.p_type {

            program_header::PT_LOAD => {
                has_pt_load = true;
                // Segment offsets: rounds down the segment start to a value suitable for mmaping, and adjusts the size of the
                // mmap breadth appropriately
                let seg_start: u64 = phdr.p_vaddr + load_bias;
                let seg_end: u64   = seg_start + phdr.p_memsz;

                let seg_page_start: u64 = page::page_start(seg_start);
                let seg_page_end: u64   = page::page_start(seg_end);

                // TODO: unused, I think we need to zero some stuff
                let seg_file_end: u64 = seg_start + phdr.p_filesz;

                // File offsets.
                let file_start: u64 = phdr.p_offset;
                let file_end: u64   = file_start + phdr.p_filesz;

                // "rounds" to an mmap-able value (i.e., file_start % pagesize)
                // file_page_start <= file_start
                // so sometimes the beginning of the page is not the beginning of the PT_LOAD!
                let file_page_start = page::page_start(file_start);
                let file_length: u64 = file_end - file_page_start;

                // TODO: add error checking, if file size <= 0, if file_end greater than file_size, etc.

                dbgc!(red_bold: debug, "loader", "PT_LOAD:\n\tseg_start: {:x} seg_end: {:x} seg_page_start: {:x} seg_page_end: {:x} seg_file_end: {:x}\n\tfile_start: {:x} file_end: {:x} file_page_start: {:x} file_length: {:x}", seg_start, seg_end, seg_page_start, seg_page_end, seg_file_end, file_start, file_end, file_page_start, file_length);

                if file_length != 0 {
                    let mmap_flags = mmap::MAP_FIXED | mmap::MAP_PRIVATE;
                    let prot_flags = pflags_to_prot(phdr.p_flags);
                    unsafe {
                        let start = mmap::mmap(seg_page_start as *const u64,
                                               file_length as usize,
                                               prot_flags,
                                               mmap_flags as c_int,
                                               fd.as_raw_fd() as c_int,
                                               file_page_start as usize);

                        if start == mmap::MAP_FAILED {

                            return Err(format!("Error: loader loading phdrs for {} failed with errno {}, aborting execution", &soname, utils::get_errno()))
                        }
                    }

                    // TODO: other more boring shit to do with zero'd out extra pages if too big, etc.
                    //seg_file_end = page::page_end(seg_file_end);

                }
            } // end match PT_LOAD

            program_header::PT_PHDR => {
                phdrs_vaddr = phdr.p_vaddr;
            },
            program_header::PT_DYNAMIC => {
                dynamic_vaddr = Some(phdr.p_vaddr);
            },
            _ => () // do nothing, i.e., continue
        }
    }

    if !has_pt_load {
        return Err(format!("Error: loader {} has no PT_LOAD sections", soname));
    }

    ///////////////
    // Part Deux:
    //   wherein we construct our components for this SharedObject
    //   from the newly mmap'd memory
    ///////////////

    // use the now mmap'd program headers
    let phdrs = unsafe { program_header::ProgramHeader::from_raw_parts((phdrs_vaddr + load_bias) as *const program_header::ProgramHeader, phdrs.len()) };

    // construct the dynamic slice in whatever mmap'd PT_LOAD it's in
    let dynamic_vaddr = dynamic_vaddr.ok_or(format!("Error: loader {} has no dynamic array", soname))?;
    let dynamic = unsafe { dyn::from_raw(load_bias, dynamic_vaddr) };

    // build the link info with the bias and the dynamic array
    let link_info = dyn::LinkInfo::new(&dynamic, load_bias as usize);

    // now get the strtab from the dynamic array
    let strtab = unsafe { Strtab::from_raw(link_info.strtab as *const u8, link_info.strsz as usize) };

    // now get the libs we will need
    let libs = dyn::get_needed(dynamic, &strtab, link_info.needed_count);

    // caveat about rdr doing this for hundres of binaries and it being "ok"
    let num_syms = (link_info.strtab - link_info.symtab) / sym::SIZEOF_SYM;

    // now construct the symtab
    let symtab = unsafe { sym::from_raw(link_info.symtab as *const sym::Sym, num_syms) };

    // now grab relatab, and pltreltab which we'll use for relocating this shared object later
    let relatab = unsafe { rela::from_raw(link_info.rela as *const rela::Rela, link_info.relasz) };
    let pltrelatab = unsafe { rela::from_raw(link_info.jmprel as *const rela::Rela, link_info.pltrelsz) };

    // the pltgot we need for doing lazy dynamic linking
    let pltgot = if let Some(addr) = link_info.pltgot { addr } else { 0 }; // musl doesn't have a PLTGOT, for example

    // and finally grab the gnu_hash (if it has one)
    let gnu_hash = if let Some(addr) = link_info.gnu_hash { Some (GnuHash::new(addr as *const u32, symtab.len())) } else { None };

    let shared_object = SharedObject {
        name: strtab.get(link_info.soname),
        load_bias: load_bias,
        libs: libs,
        map_begin: start,
        map_end: end,
        phdrs: phdrs,
        dynamic: dynamic,
        symtab: symtab,
        strtab: strtab,
        relatab: relatab,
        pltrelatab: pltrelatab,
        pltgot: pltgot as *const u64,
        gnu_hash: gnu_hash,
        load_path: Some (load_path),
        flags: link_info.flags,
        state_flags: link_info.flags_1,
    };

    Ok (shared_object)
}
<|endoftext|>


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

// Format list-like macro invocations. These are invocations whose token trees
// can be interpreted as expressions and separated by commas.
// Note that these token trees do not actually have to be interpreted as
// expressions by the compiler. An example of an invocation we would reformat is
// foo!( x, y, z ). The token x may represent an identifier in the code, but we
// interpreted as an expression.
// Macro uses which are not-list like, such as bar!(key => val), will not be
// reformated.
// List-like invocations with parentheses will be formatted as function calls,
// and those with brackets will be formatted as array literals.

use syntax::ast;
use syntax::parse::token::Token;
use syntax::parse::tts_to_parser;
use syntax::codemap::{mk_sp, BytePos};

use Indent;
use rewrite::RewriteContext;
use expr::{rewrite_call, rewrite_array};
use comment::{FindUncommented, contains_comment};
use utils::{CodeMapSpanUtils, wrap_str};

const FORCED_BRACKET_MACROS: &'static [&'static str] = &["vec!"];

// FIXME: use the enum from libsyntax?
#[derive(Clone, Copy, PartialEq, Eq)]
enum MacroStyle {
    Parens,
    Brackets,
    Braces,
}

impl MacroStyle {
    fn opener(&self) -> &'static str {
        match *self {
            MacroStyle::Parens => "(",
            MacroStyle::Brackets => "[",
            MacroStyle::Braces => "{",
        }
    }
}

pub fn rewrite_macro(mac: &ast::Mac,
                     extra_ident: Option<ast::Ident>,
                     context: &RewriteContext,
                     width: usize,
                     offset: Indent)
                     -> Option<String> {
    let original_style = macro_style(mac, context);
    let macro_name = match extra_ident {
        None | Some(ast::Ident { name: ast::Name(0), .. }) => format!("{}!", mac.node.path),
        Some(ident) => format!("{}! {}", mac.node.path, ident),
    };
    let style = if FORCED_BRACKET_MACROS.contains(&&macro_name[..]) {
        MacroStyle::Brackets
    } else {
        original_style
    };

    if mac.node.tts.is_empty() && !contains_comment(&context.snippet(mac.span)) {
        return match style {
            MacroStyle::Parens => Some(format!("{}()", macro_name)),
            MacroStyle::Brackets => Some(format!("{}[]", macro_name)),
            MacroStyle::Braces => Some(format!("{}{{}}", macro_name)),
        };
    }

    let mut parser = tts_to_parser(context.parse_session, mac.node.tts.clone(), Vec::new());
    let mut expr_vec = Vec::new();

    if MacroStyle::Braces != style {
        loop {
            expr_vec.push(match parser.parse_expr() {
                Ok(expr) => expr,
                Err(..) => return None,
            });

            match parser.token {
                Token::Eof => break,
                Token::Comma => (),
                _ => return None,
            }

            let _ = parser.bump();

            if parser.token == Token::Eof {
                return None;
            }
        }
    }

    match style {
        MacroStyle::Parens => {
            // Format macro invocation as function call.
            rewrite_call(context, &macro_name, &expr_vec, mac.span, width, offset)
        }
        MacroStyle::Brackets => {
            // Format macro invocation as array literal.
            let extra_offset = macro_name.len();
            let rewrite = try_opt!(rewrite_array(expr_vec.iter().map(|x| &**x),
                                                 mk_sp(context.codemap.span_after(mac.span,
                                                                  original_style.opener()),
                                                       mac.span.hi - BytePos(1)),
                                                 context,
                                                 try_opt!(width.checked_sub(extra_offset)),
                                                 offset + extra_offset));

            Some(format!("{}{}", macro_name, rewrite))
        }
        MacroStyle::Braces => {
            // Skip macro invocations with braces, for now.
            wrap_str(context.snippet(mac.span),
                     context.config.max_width,
                     width,
                     offset)
        }
    }
}

fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> MacroStyle {
    let snippet = context.snippet(mac.span);
    let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value());
    let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value());
    let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value());

    if paren_pos < bracket_pos && paren_pos < brace_pos {
        MacroStyle::Parens
    } else if bracket_pos < brace_pos {
        MacroStyle::Brackets
    } else {
        MacroStyle::Braces
    }
}
<commit_msg>macros: Cancel DiagnosticBuilder when not emitting error<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Format list-like macro invocations. These are invocations whose token trees
// can be interpreted as expressions and separated by commas.
// Note that these token trees do not actually have to be interpreted as
// expressions by the compiler. An example of an invocation we would reformat is
// foo!( x, y, z ). The token x may represent an identifier in the code, but we
// interpreted as an expression.
// Macro uses which are not-list like, such as bar!(key => val), will not be
// reformated.
// List-like invocations with parentheses will be formatted as function calls,
// and those with brackets will be formatted as array literals.

use syntax::ast;
use syntax::parse::token::Token;
use syntax::parse::tts_to_parser;
use syntax::codemap::{mk_sp, BytePos};

use Indent;
use rewrite::RewriteContext;
use expr::{rewrite_call, rewrite_array};
use comment::{FindUncommented, contains_comment};
use utils::{CodeMapSpanUtils, wrap_str};

const FORCED_BRACKET_MACROS: &'static [&'static str] = &["vec!"];

// FIXME: use the enum from libsyntax?
#[derive(Clone, Copy, PartialEq, Eq)]
enum MacroStyle {
    Parens,
    Brackets,
    Braces,
}

impl MacroStyle {
    fn opener(&self) -> &'static str {
        match *self {
            MacroStyle::Parens => "(",
            MacroStyle::Brackets => "[",
            MacroStyle::Braces => "{",
        }
    }
}

pub fn rewrite_macro(mac: &ast::Mac,
                     extra_ident: Option<ast::Ident>,
                     context: &RewriteContext,
                     width: usize,
                     offset: Indent)
                     -> Option<String> {
    let original_style = macro_style(mac, context);
    let macro_name = match extra_ident {
        None | Some(ast::Ident { name: ast::Name(0), .. }) => format!("{}!", mac.node.path),
        Some(ident) => format!("{}! {}", mac.node.path, ident),
    };
    let style = if FORCED_BRACKET_MACROS.contains(&&macro_name[..]) {
        MacroStyle::Brackets
    } else {
        original_style
    };

    if mac.node.tts.is_empty() && !contains_comment(&context.snippet(mac.span)) {
        return match style {
            MacroStyle::Parens => Some(format!("{}()", macro_name)),
            MacroStyle::Brackets => Some(format!("{}[]", macro_name)),
            MacroStyle::Braces => Some(format!("{}{{}}", macro_name)),
        };
    }

    let mut parser = tts_to_parser(context.parse_session, mac.node.tts.clone(), Vec::new());
    let mut expr_vec = Vec::new();

    if MacroStyle::Braces != style {
        loop {
            expr_vec.push(match parser.parse_expr() {
                Ok(expr) => expr,
                Err(mut e) => {
                    e.cancel();
                    return None;
                }
            });

            match parser.token {
                Token::Eof => break,
                Token::Comma => (),
                _ => return None,
            }

            let _ = parser.bump();

            if parser.token == Token::Eof {
                return None;
            }
        }
    }

    match style {
        MacroStyle::Parens => {
            // Format macro invocation as function call.
            rewrite_call(context, &macro_name, &expr_vec, mac.span, width, offset)
        }
        MacroStyle::Brackets => {
            // Format macro invocation as array literal.
            let extra_offset = macro_name.len();
            let rewrite = try_opt!(rewrite_array(expr_vec.iter().map(|x| &**x),
                                                 mk_sp(context.codemap.span_after(mac.span,
                                                                  original_style.opener()),
                                                       mac.span.hi - BytePos(1)),
                                                 context,
                                                 try_opt!(width.checked_sub(extra_offset)),
                                                 offset + extra_offset));

            Some(format!("{}{}", macro_name, rewrite))
        }
        MacroStyle::Braces => {
            // Skip macro invocations with braces, for now.
            wrap_str(context.snippet(mac.span),
                     context.config.max_width,
                     width,
                     offset)
        }
    }
}

fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> MacroStyle {
    let snippet = context.snippet(mac.span);
    let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value());
    let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value());
    let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value());

    if paren_pos < bracket_pos && paren_pos < brace_pos {
        MacroStyle::Parens
    } else if bracket_pos < brace_pos {
        MacroStyle::Brackets
    } else {
        MacroStyle::Braces
    }
}
<|endoftext|>


<commit_before>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2015  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#[macro_export]
macro_rules! g_str {
    ($lit:expr) => {
        $crate::gstr::GStr::from_static_bytes(concat!($lit, "\0").as_bytes())
    }
}

#[macro_export]
macro_rules! g_utf8 {
    ($lit:expr) => {
        $crate::gstr::Utf8::from_static_str(concat!($lit, "\0"))
    }
}

#[macro_export]
macro_rules! g_static_quark {
    ($lit:expr) => {
        {
            use $crate::quark::StaticQuark;

            static QUARK: StaticQuark =
                StaticQuark($lit,
                            std::sync::atomic::ATOMIC_UINT_INIT);

            QUARK.get()
        }
    }
}
<commit_msg>macros: cosmetic nitpick<commit_after>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2015  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#[macro_export]
macro_rules! g_str {
    ($lit:expr) => {
        $crate::gstr::GStr::from_static_bytes(concat!($lit, "\0").as_bytes())
    }
}

#[macro_export]
macro_rules! g_utf8 {
    ($lit:expr) => {
        $crate::gstr::Utf8::from_static_str(concat!($lit, "\0"))
    }
}

#[macro_export]
macro_rules! g_static_quark {
    ($lit:expr) => {
        {
            use $crate::quark::StaticQuark;

            static QUARK: StaticQuark =
                StaticQuark($lit, std::sync::atomic::ATOMIC_UINT_INIT);

            QUARK.get()
        }
    }
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! as_item { ($i:item) => {$i} }

// Required because if-let can't be used with irrefutable patterns, so it needs to be special
// cased.
#[macro_export]
macro_rules! request_fns {
    ($fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty) => (
        pub fn $fn_name(&self, $($arg: $in_),*) -> $crate::Result<$out> {
            let reply = try!((self.0).rpc(&request_variant!($fn_name $($arg),*)));
            let Reply::$fn_name(reply) = reply;
            Ok(reply)
        }
    );
    ($( $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty)*) => ( $(
        pub fn $fn_name(&self, $($arg: $in_),*) -> $crate::Result<$out> {
            let reply = try!((self.0).rpc(&request_variant!($fn_name $($arg),*)));
            if let Reply::$fn_name(reply) = reply {
                Ok(reply)
            } else {
                Err($crate::Error::InternalError)
            }
        }
    )*);
}

// Required because enum variants with no fields can't be suffixed by parens
#[macro_export]
macro_rules! define_request {
    ($(@($($finished:tt)*))* --) => (as_item!(
            #[allow(non_camel_case_types)]
            #[derive(Debug, Serialize, Deserialize)]
            enum Request { $($($finished)*),* }
    ););
    ($(@$finished:tt)* -- $name:ident() $($req:tt)*) =>
        (define_request!($(@$finished)* @($name) -- $($req)*););
    ($(@$finished:tt)* -- $name:ident $args: tt $($req:tt)*) =>
        (define_request!($(@$finished)* @($name $args) -- $($req)*););
    ($($started:tt)*) => (define_request!(-- $($started)*););
}

// Required because enum variants with no fields can't be suffixed by parens
#[macro_export]
macro_rules! request_variant {
    ($x:ident) => (Request::$x);
    ($x:ident $($y:ident),+) => (Request::$x($($y),+));
}

// The main macro that creates RPC services.
#[macro_export]
macro_rules! rpc_service { ($server:ident:
    $( $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty;)*) => {
        #[doc="A module containing an rpc service and client stub."]
        pub mod $server {
            use std::net::ToSocketAddrs;
            use std::sync::Arc;
            use $crate::protocol::{
                self,
                ServeHandle,
                serve_async,
            };

            #[doc="The provided RPC service."]
            pub trait Service: Send + Sync {
                $(
                    fn $fn_name(&self, $($arg:$in_),*) -> $out;
                )*
            }

            define_request!($($fn_name($($in_),*))*);

            #[allow(non_camel_case_types)]
            #[derive(Debug, Serialize, Deserialize)]
            enum Reply {
                $(
                    $fn_name($out),
                )*
            }

            #[doc="The client stub that makes RPC calls to the server."]
            pub struct Client(protocol::Client<Request, Reply>);

            impl Client {
                #[doc="Create a new client that connects to the given address."]
                pub fn new<A>(addr: A) -> $crate::Result<Self>
                    where A: ToSocketAddrs,
                {
                    let inner = try!(protocol::Client::new(addr));
                    Ok(Client(inner))
                }

                request_fns!($($fn_name($($arg: $in_),*) -> $out)*);
            }

            struct Server<S: 'static + Service>(S);

            impl<S> protocol::Serve<Request, Reply> for Server<S>
                where S: 'static + Service
            {
                fn serve(&self, request: Request) -> Reply {
                    match request {
                        $(
                            request_variant!($fn_name $($arg),*) =>
                                Reply::$fn_name((self.0).$fn_name($($arg),*)),
                         )*
                    }
                }
            }

            #[doc="Start a running service."]
            pub fn serve<A, S>(addr: A, service: S) -> $crate::Result<ServeHandle>
                where A: ToSocketAddrs,
                      S: 'static + Service
            {
                let server = Arc::new(Server(service));
                Ok(try!(serve_async(addr, server)))
            }
        }
    }
}

#[cfg(test)]
#[allow(dead_code)]
mod test {

    rpc_service!(my_server:
        hello(foo: super::Foo) -> super::Foo;

        add(x: i32, y: i32) -> i32;
    );

    use self::my_server::*;

    #[derive(PartialEq, Debug, Serialize, Deserialize)]
    pub struct Foo {
        message: String
    }

    impl Service for () {
        fn hello(&self, s: Foo) -> Foo {
            Foo{message: format!("Hello, {}", &s.message)}
        }

        fn add(&self, x: i32, y: i32) -> i32 {
            x + y
        }
    }

    #[test]
    fn simple_test() {
        println!("Starting");
        let addr = "127.0.0.1:9000";
        let shutdown = my_server::serve(addr, ()).unwrap();
        let client = Client::new(addr).unwrap();
        assert_eq!(3, client.add(1, 2).unwrap());
        let foo = Foo{message: "Adam".into()};
        let want = Foo{message: format!("Hello, {}", &foo.message)};
        assert_eq!(want, client.hello(Foo{message: "Adam".into()}).unwrap());
        drop(client);
        shutdown.shutdown();
    }

    // This is a test of a service with a fn that takes no args
    rpc_service! {foo:
        hello() -> String;
    }
}
<commit_msg>Obfuscate types internal to the generated module to reduce risk of collision with imports.<commit_after>#[macro_export]
macro_rules! as_item { ($i:item) => {$i} }

// Required because if-let can't be used with irrefutable patterns, so it needs to be special
// cased.
#[macro_export]
macro_rules! request_fns {
    ($fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty) => (
        pub fn $fn_name(&self, $($arg: $in_),*) -> $crate::Result<$out> {
            let reply = try!((self.0).rpc(&request_variant!($fn_name $($arg),*)));
            let __Reply::$fn_name(reply) = reply;
            Ok(reply)
        }
    );
    ($( $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty)*) => ( $(
        pub fn $fn_name(&self, $($arg: $in_),*) -> $crate::Result<$out> {
            let reply = try!((self.0).rpc(&request_variant!($fn_name $($arg),*)));
            if let __Reply::$fn_name(reply) = reply {
                Ok(reply)
            } else {
                Err($crate::Error::InternalError)
            }
        }
    )*);
}

// Required because enum variants with no fields can't be suffixed by parens
#[macro_export]
macro_rules! define_request {
    ($(@($($finished:tt)*))* --) => (as_item!(
            #[allow(non_camel_case_types)]
            #[derive(Debug, Serialize, Deserialize)]
            enum __Request { $($($finished)*),* }
    ););
    ($(@$finished:tt)* -- $name:ident() $($req:tt)*) =>
        (define_request!($(@$finished)* @($name) -- $($req)*););
    ($(@$finished:tt)* -- $name:ident $args: tt $($req:tt)*) =>
        (define_request!($(@$finished)* @($name $args) -- $($req)*););
    ($($started:tt)*) => (define_request!(-- $($started)*););
}

// Required because enum variants with no fields can't be suffixed by parens
#[macro_export]
macro_rules! request_variant {
    ($x:ident) => (__Request::$x);
    ($x:ident $($y:ident),+) => (__Request::$x($($y),+));
}

// The main macro that creates RPC services.
#[macro_export]
macro_rules! rpc_service { ($server:ident:
    $( $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty;)*) => {
        #[doc="A module containing an rpc service and client stub."]
        pub mod $server {

            #[doc="The provided RPC service."]
            pub trait Service: Send + Sync {
                $(
                    fn $fn_name(&self, $($arg:$in_),*) -> $out;
                )*
            }

            define_request!($($fn_name($($in_),*))*);

            #[allow(non_camel_case_types)]
            #[derive(Debug, Serialize, Deserialize)]
            enum __Reply {
                $(
                    $fn_name($out),
                )*
            }

            #[doc="The client stub that makes RPC calls to the server."]
            pub struct Client($crate::protocol::Client<__Request, __Reply>);

            impl Client {
                #[doc="Create a new client that connects to the given address."]
                pub fn new<A>(addr: A) -> $crate::Result<Self>
                    where A: ::std::net::ToSocketAddrs,
                {
                    let inner = try!($crate::protocol::Client::new(addr));
                    Ok(Client(inner))
                }

                request_fns!($($fn_name($($arg: $in_),*) -> $out)*);
            }

            struct __Server<S: 'static + Service>(S);

            impl<S> $crate::protocol::Serve<__Request, __Reply> for __Server<S>
                where S: 'static + Service
            {
                fn serve(&self, request: __Request) -> __Reply {
                    match request {
                        $(
                            request_variant!($fn_name $($arg),*) =>
                                __Reply::$fn_name((self.0).$fn_name($($arg),*)),
                         )*
                    }
                }
            }

            #[doc="Start a running service."]
            pub fn serve<A, S>(addr: A, service: S) -> $crate::Result<$crate::protocol::ServeHandle>
                where A: ::std::net::ToSocketAddrs,
                      S: 'static + Service
            {
                let server = ::std::sync::Arc::new(__Server(service));
                Ok(try!($crate::protocol::serve_async(addr, server)))
            }
        }
    }
}

#[cfg(test)]
#[allow(dead_code)]
mod test {
    rpc_service!(my_server:
        hello(foo: super::Foo) -> super::Foo;

        add(x: i32, y: i32) -> i32;
    );

    use self::my_server::*;

    #[derive(PartialEq, Debug, Serialize, Deserialize)]
    pub struct Foo {
        message: String
    }

    impl Service for () {
        fn hello(&self, s: Foo) -> Foo {
            Foo{message: format!("Hello, {}", &s.message)}
        }

        fn add(&self, x: i32, y: i32) -> i32 {
            x + y
        }
    }

    #[test]
    fn simple_test() {
        println!("Starting");
        let addr = "127.0.0.1:9000";
        let shutdown = my_server::serve(addr, ()).unwrap();
        let client = Client::new(addr).unwrap();
        assert_eq!(3, client.add(1, 2).unwrap());
        let foo = Foo{message: "Adam".into()};
        let want = Foo{message: format!("Hello, {}", &foo.message)};
        assert_eq!(want, client.hello(Foo{message: "Adam".into()}).unwrap());
        drop(client);
        shutdown.shutdown();
    }

    // This is a test of a service with a fn that takes no args
    rpc_service! {foo:
        hello() -> String;
    }
}
<|endoftext|>


<commit_before>use byteorder::{BigEndian, ByteOrder};

/// Glulx magic number
const MAGIC_NUMBER: u32 = 0x476C756C;

/// Minimum supported glulx target
const MIN_VERSION: u32 = 0x0020000;

/// Maximum supported glulx target
const MAX_VERSION: u32 = 0x00301FF;

/// Struct representing a glulx memory object.
pub struct GlulxMemory {
    heap_mode: bool,
    memory: Vec<u8>,
}

impl GlulxMemory {

    /// This takes a rom, validates it to verify that it is a valid
    /// Glulx rom, and retuns either an error string or a `GlulxMemory`
    /// TODO: Replace error string with custom glulx error type.
    pub fn from_rom(rom: Vec<u8>) -> Result<GlulxMemory, String> {
        // Validate magic number.
        let magic_number = BigEndian::read_u32(&rom[0x0..0x4]);
        if magic_number != MAGIC_NUMBER {
            return Err("executable code starts with invalid magic number"
                .to_string());
        }

        // Validate glulx version.
        let glulx_version = BigEndian::read_u32(&rom[0x4..0x8]);
        if glulx_version < MIN_VERSION {
            return Err("executable code glulx version is less than 2.0.0"
                .to_string());
        } else if glulx_version > MAX_VERSION {
            return Err("executable code glulx version is greater than 3.1.x"
                .to_string());
        }

        // Validate ramstart value.
        let ramstart = BigEndian::read_u32(&rom[0x8..0xC]);
        if ramstart < 0x100 {
            return Err("ramstart is less than 0x100 bytes".to_string());
        } else if ramstart % 0x100 != 0 {
            return Err("ramstart is misaligned".to_string());
        }

        // Validate extstart value.
        let extstart = BigEndian::read_u32(&rom[0xC..0x10]);
        if extstart < ramstart {
            return Err("extstart is less than ramstart".to_string());
        } else if extstart % 0x100 != 0 {
            return Err("extstart is misaligned".to_string());
        }

        // Validate endmem value.
        let endmem = BigEndian::read_u32(&rom[0x10..0x14]);
        if endmem < extstart {
            return Err("endmem is less than extstart".to_string());
        } else if endmem % 0x100 != 0 {
            return Err("endmem is misaligned".to_string());
        }

        let mut rom = rom;
        let ext_size = (endmem - extstart) as usize;
        let size = rom.len() + ext_size;
        rom.reserve_exact(ext_size);
        rom.resize(size, 0x0);

        Ok(GlulxMemory { heap_mode: false, memory: rom })
    }

    pub fn zero_range(&mut self, size: u32, ptr: u32){
        for i in ptr as usize..(ptr + size) as usize {
            self.memory[i] = 0;
        }
    }

    pub fn copy_range(&mut self, size: u32, from_ptr: u32, to_ptr: u32){
        let to_ptr = to_ptr as usize;
        let from_ptr = from_ptr as usize;

        for i in 0x0..size as usize {
            self.memory[to_ptr + i] = self.memory[from_ptr + i];
        }
    }

    // Header value functions.

    /// The glulx magic number, stored from `0x0..0x4` in the header.
    /// This number is unique to all glulx games. This value should be
    /// `0x476C756C`, which is equivalent to `b"Glul"`.
    fn magic_number(&self) -> u32 {
        BigEndian::read_u32(&self.memory[..0x4])
    }

    /// The glulx version number, stored from `0x4..0x8` in the header.
    /// First two bytes store the major version number. The next byte
    /// stores the minor version number. The final byte stores the patch
    /// version number.
    fn glulx_version(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x4..0x8])
    }

    /// The address indicating the start of the RAM, stored from
    /// `0x4..0x8` in the header. TODO: MOAR DATA
    fn ramstart(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x8..0xC])
    }

    fn extstart(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0xC..0x10])
    }

    fn endmem(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x10..0x14])
    }

    pub fn stack_size(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x14..0x18])
    }

    pub fn start_func(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x18..0x1C])
    }

    fn decoding_tbl(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x1C..0x20])
    }

    /// The sum of the intial contents of memory, considered as an array
    /// of `u32`s. When calculated, the checksum value is considered to
    /// be `0`.
    fn checksum(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x20..0x24])
    }

    pub fn get_mem_size(&self) -> u32 {
        self.memory.len() as u32
    }

    /// Sets the memory size to the given value. This call only works if
    /// there is no active heap, the given value is a multiple of 0x100,
    /// and the value is greater than the ENDMEM value identified in the
    /// header.
    pub fn set_mem_size(&mut self, value: u32) -> u32 {
        if self.heap_mode
                && value % 0x100 == 0
                && value >= self.endmem() {
            self.memory.resize(value as usize, 0x0);
            0
        } else {
            1
        }
    }
}

pub trait Memory<T> {
    fn read(&self, ptr: u32) -> T;
    fn write(&mut self, ptr: u32, value: T);
    fn ram_read(&self, ptr: u32) -> T;
    fn ram_write(&mut self, ptr: u32, value: T);
}

impl Memory<u8> for GlulxMemory {
    fn read(&self, ptr: u32) -> u8 {
        self.memory[ptr as usize]
    }

    fn write(&mut self, ptr: u32, value: u8) {
        self.memory[ptr as usize] = value
    }

    fn ram_read(&self, ptr: u32) -> u8 {
        let ptr = (ptr + self.ramstart()) as usize;
        self.memory[ptr]
    }

    fn ram_write(&mut self, ptr: u32, value: u8) {
        let ptr = (ptr + self.ramstart()) as usize;
        self.memory[ptr] = value
    }
}

impl Memory<u16> for GlulxMemory {
    fn read(&self, ptr: u32) -> u16 {
        BigEndian::read_u16(&self.memory[ptr as usize..])
    }

    fn write(&mut self, ptr: u32, value: u16) {
        BigEndian::write_u16(&mut self.memory[ptr as usize..], value)
    }

    fn ram_read(&self, ptr: u32) -> u16 {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::read_u16(&self.memory[ptr..])
    }

    fn ram_write(&mut self, ptr: u32, value: u16) {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::write_u16(&mut self.memory[ptr..], value)
    }
}

impl Memory<u32> for GlulxMemory {
    fn read(&self, ptr: u32) -> u32 {
        BigEndian::read_u32(&self.memory[ptr as usize..])
    }

    fn write(&mut self, ptr: u32, value: u32) {
        BigEndian::write_u32(&mut self.memory[ptr as usize..], value)
    }

    fn ram_read(&self, ptr: u32) -> u32 {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::read_u32(&self.memory[ptr..])
    }

    fn ram_write(&mut self, ptr: u32, value: u32) {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::write_u32(&mut self.memory[ptr..], value)
    }
}

impl Memory<i32> for GlulxMemory {
    fn read(&self, ptr: u32) -> i32 {
        BigEndian::read_i32(&self.memory[ptr as usize..])
    }

    fn write(&mut self, ptr: u32, value: i32) {
        BigEndian::write_i32(&mut self.memory[ptr as usize..], value)
    }

    fn ram_read(&self, ptr: u32) -> i32 {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::read_i32(&self.memory[ptr..])
    }

    fn ram_write(&mut self, ptr: u32, value: i32) {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::write_i32(&mut self.memory[ptr..], value)
    }
}

impl Memory<f32> for GlulxMemory {
    fn read(&self, ptr: u32) -> f32 {
        BigEndian::read_f32(&self.memory[ptr as usize..])
    }

    fn write(&mut self, ptr: u32, value: f32) {
        BigEndian::write_f32(&mut self.memory[ptr as usize..], value)
    }

    fn ram_read(&self, ptr: u32) -> f32 {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::read_f32(&self.memory[ptr..])
    }

    fn ram_write(&mut self, ptr: u32, value: f32) {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::write_f32(&mut self.memory[ptr..], value)
    }
}
<commit_msg>added checksum, endmem to rom length, and stack size is multiple of 0x100 checks to memory initialization<commit_after>use byteorder::{BigEndian, ByteOrder};

/// Glulx magic number
const MAGIC_NUMBER: u32 = 0x476C756C;

/// Minimum supported glulx target
const MIN_VERSION: u32 = 0x0020000;

/// Maximum supported glulx target
const MAX_VERSION: u32 = 0x00301FF;

/// Struct representing a glulx memory object.
pub struct GlulxMemory {
    heap_mode: bool,
    memory: Vec<u8>,
}

impl GlulxMemory {

    /// This takes a rom, validates it to verify that it is a valid
    /// Glulx rom, and retuns either an error string or a `GlulxMemory`
    /// TODO: Replace error string with custom glulx error type.
    pub fn from_rom(rom: Vec<u8>) -> Result<GlulxMemory, String> {
        // Validate magic number.
        let magic_number = BigEndian::read_u32(&rom[0x0..0x4]);
        if magic_number != MAGIC_NUMBER {
            return Err("executable code starts with invalid magic number"
                .to_string());
        }

        // Validate glulx version.
        let glulx_version = BigEndian::read_u32(&rom[0x4..0x8]);
        if glulx_version < MIN_VERSION {
            return Err("executable code glulx version is less than 2.0.0"
                .to_string());
        } else if glulx_version > MAX_VERSION {
            return Err("executable code glulx version is greater than 3.1.x"
                .to_string());
        }

        // Validate ramstart value.
        let ramstart = BigEndian::read_u32(&rom[0x8..0xC]);
        if ramstart < 0x100 {
            return Err("ramstart is less than 0x100 bytes".to_string());
        } else if ramstart % 0x100 != 0 {
            return Err("ramstart is misaligned".to_string());
        }

        // Validate extstart value.
        let extstart = BigEndian::read_u32(&rom[0xC..0x10]);
        if extstart < ramstart {
            return Err("extstart is less than ramstart".to_string());
        } else if extstart % 0x100 != 0 {
            return Err("extstart is misaligned".to_string());
        }

        // Validate endmem value.
        let endmem = BigEndian::read_u32(&rom[0x10..0x14]);
        if endmem < extstart {
            return Err("endmem is less than extstart".to_string());
        } else if endmem % 0x100 != 0 {
            return Err("endmem is misaligned".to_string());
        }

        // Validate stack size.
        let stack_size = BigEndian::read_u32(&rom[0x14..0x18]);
        if stack_size % 0x100 != 0 {
            return Err("stack size is misaligned".to_string());
        }

        if endmem as usize != rom.len() {
            return Err("rom size is not correct".to_string());
        }

        let checksum = BigEndian::read_u32(&rom[0x20..0x24]);
        let sum = {
            let mut sum = 0u32;
            for i in 0..rom.len()/4 {
                sum = sum.wrapping_add(BigEndian::read_u32(&rom[i*4..]));
            }
            sum.wrapping_sub(checksum)
        };
        if checksum != sum {
            return Err("rom checksum is not correct".to_string());
        }

        let mut rom = rom;
        let ext_size = (endmem - extstart) as usize;
        let size = rom.len() + ext_size;
        rom.reserve_exact(ext_size);
        rom.resize(size, 0x0);

        Ok(GlulxMemory { heap_mode: false, memory: rom })
    }

    pub fn zero_range(&mut self, size: u32, ptr: u32){
        for i in ptr as usize..(ptr + size) as usize {
            self.memory[i] = 0;
        }
    }

    pub fn copy_range(&mut self, size: u32, from_ptr: u32, to_ptr: u32){
        let to_ptr = to_ptr as usize;
        let from_ptr = from_ptr as usize;

        for i in 0x0..size as usize {
            self.memory[to_ptr + i] = self.memory[from_ptr + i];
        }
    }

    // Header value functions.

    /// The glulx magic number, stored from `0x0..0x4` in the header.
    /// This number is unique to all glulx games. This value should be
    /// `0x476C756C`, which is equivalent to `b"Glul"`.
    fn magic_number(&self) -> u32 {
        BigEndian::read_u32(&self.memory[..0x4])
    }

    /// The glulx version number, stored from `0x4..0x8` in the header.
    /// First two bytes store the major version number. The next byte
    /// stores the minor version number. The final byte stores the patch
    /// version number.
    fn glulx_version(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x4..0x8])
    }

    /// The address indicating the start of the RAM, stored from
    /// `0x4..0x8` in the header. TODO: MOAR DATA
    fn ramstart(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x8..0xC])
    }

    fn extstart(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0xC..0x10])
    }

    fn endmem(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x10..0x14])
    }

    pub fn stack_size(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x14..0x18])
    }

    pub fn start_func(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x18..0x1C])
    }

    fn decoding_tbl(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x1C..0x20])
    }

    /// The sum of the intial contents of memory, considered as an array
    /// of `u32`s. When calculated, the checksum value is considered to
    /// be `0`.
    fn checksum(&self) -> u32 {
        BigEndian::read_u32(&self.memory[0x20..0x24])
    }

    pub fn get_mem_size(&self) -> u32 {
        self.memory.len() as u32
    }

    /// Sets the memory size to the given value. This call only works if
    /// there is no active heap, the given value is a multiple of 0x100,
    /// and the value is greater than the ENDMEM value identified in the
    /// header.
    pub fn set_mem_size(&mut self, value: u32) -> u32 {
        if self.heap_mode
                && value % 0x100 == 0
                && value >= self.endmem() {
            self.memory.resize(value as usize, 0x0);
            0
        } else {
            1
        }
    }
}

pub trait Memory<T> {
    fn read(&self, ptr: u32) -> T;
    fn write(&mut self, ptr: u32, value: T);
    fn ram_read(&self, ptr: u32) -> T;
    fn ram_write(&mut self, ptr: u32, value: T);
}

impl Memory<u8> for GlulxMemory {
    fn read(&self, ptr: u32) -> u8 {
        self.memory[ptr as usize]
    }

    fn write(&mut self, ptr: u32, value: u8) {
        self.memory[ptr as usize] = value
    }

    fn ram_read(&self, ptr: u32) -> u8 {
        let ptr = (ptr + self.ramstart()) as usize;
        self.memory[ptr]
    }

    fn ram_write(&mut self, ptr: u32, value: u8) {
        let ptr = (ptr + self.ramstart()) as usize;
        self.memory[ptr] = value
    }
}

impl Memory<u16> for GlulxMemory {
    fn read(&self, ptr: u32) -> u16 {
        BigEndian::read_u16(&self.memory[ptr as usize..])
    }

    fn write(&mut self, ptr: u32, value: u16) {
        BigEndian::write_u16(&mut self.memory[ptr as usize..], value)
    }

    fn ram_read(&self, ptr: u32) -> u16 {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::read_u16(&self.memory[ptr..])
    }

    fn ram_write(&mut self, ptr: u32, value: u16) {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::write_u16(&mut self.memory[ptr..], value)
    }
}

impl Memory<u32> for GlulxMemory {
    fn read(&self, ptr: u32) -> u32 {
        BigEndian::read_u32(&self.memory[ptr as usize..])
    }

    fn write(&mut self, ptr: u32, value: u32) {
        BigEndian::write_u32(&mut self.memory[ptr as usize..], value)
    }

    fn ram_read(&self, ptr: u32) -> u32 {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::read_u32(&self.memory[ptr..])
    }

    fn ram_write(&mut self, ptr: u32, value: u32) {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::write_u32(&mut self.memory[ptr..], value)
    }
}

impl Memory<i32> for GlulxMemory {
    fn read(&self, ptr: u32) -> i32 {
        BigEndian::read_i32(&self.memory[ptr as usize..])
    }

    fn write(&mut self, ptr: u32, value: i32) {
        BigEndian::write_i32(&mut self.memory[ptr as usize..], value)
    }

    fn ram_read(&self, ptr: u32) -> i32 {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::read_i32(&self.memory[ptr..])
    }

    fn ram_write(&mut self, ptr: u32, value: i32) {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::write_i32(&mut self.memory[ptr..], value)
    }
}

impl Memory<f32> for GlulxMemory {
    fn read(&self, ptr: u32) -> f32 {
        BigEndian::read_f32(&self.memory[ptr as usize..])
    }

    fn write(&mut self, ptr: u32, value: f32) {
        BigEndian::write_f32(&mut self.memory[ptr as usize..], value)
    }

    fn ram_read(&self, ptr: u32) -> f32 {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::read_f32(&self.memory[ptr..])
    }

    fn ram_write(&mut self, ptr: u32, value: f32) {
        let ptr = (ptr + self.ramstart()) as usize;
        BigEndian::write_f32(&mut self.memory[ptr..], value)
    }
}
<|endoftext|>