<commit_before>use stb_image::*;
use stb_image::bindgen::*;
use libc::types::os::arch::c95::c_int;
use ptr::{is_null, to_unsafe_ptr};
use str::as_c_str;
use vec::as_imm_buf;
use vec::raw::from_buf_raw;

pub struct Image<T> {
    width   : uint,
    height  : uint,
    depth   : uint,
    data    : ~[T],
}

pub fn new_image<T>(width: uint, height: uint, depth: uint, data: ~[T]) -> Image<T> {
    Image::<T> {
        width   : width,
        height  : height,
        depth   : depth,
        data    : move data,
    }
}

pub fn load(path: ~str) -> Option<Image<u8>> {
    unsafe {
        let force_depth = 4;
        load_with_depth(move path, force_depth)
    }
}

pub fn load_with_depth(path: ~str, force_depth: uint) -> Option<Image<u8>> {
    unsafe {
        do task::unkillable {
            let mut width = 0 as c_int;
            let mut height = 0 as c_int;
            let mut depth = 0 as c_int;
            let buffer = as_c_str(path, |bytes| {
                stbi_load(bytes, to_unsafe_ptr(&width), to_unsafe_ptr(&height),
                          to_unsafe_ptr(&depth), force_depth as c_int)
            });

            if is_null(buffer) {
                None
            } else {
                // FIXME: Shouldn't copy; instead we should use a sendable resource. They
                // aren't particularly safe yet though.
                let data = from_buf_raw(buffer, ((width * height) as uint) * force_depth);
                libc::free(buffer as *c_void);
                Some(new_image(width as uint, height as uint, force_depth, move data))
            }
        }
    }
}

pub fn load_from_memory(buffer: &[u8]) -> Option<Image<u8>> {
    unsafe {
        let force_depth = 4;
        load_from_memory_with_depth(buffer, force_depth)
    }
}

pub fn load_from_memory_with_depth(buffer: &[u8], force_depth: uint) -> Option<Image<u8>> {
    unsafe {
        do task::unkillable {
            let mut width = 0 as c_int;
            let mut height = 0 as c_int;
            let mut depth = 0 as c_int;
            let buffer = as_imm_buf(buffer, |bytes, len| {
                stbi_load_from_memory(bytes, len as c_int, to_unsafe_ptr(&width),
                                      to_unsafe_ptr(&height), to_unsafe_ptr(&depth),
                                      force_depth as c_int)
            });

            if is_null(buffer) {
                None
            } else {
                // FIXME: Shouldn't copy; instead we should use a sendable resource. They
                // aren't particularly safe yet though.
                let data = from_buf_raw(buffer, ((width * height) as uint) * force_depth);
                libc::free(buffer as *c_void);
                Some(new_image(width as uint, height as uint, force_depth, move data))
            }
        }
    }
}

<commit_msg>Revert "Update for language changes". Reverted a bunch of HDR stuff.<commit_after>use stb_image::*;
use stb_image::bindgen::*;
use libc::types::os::arch::c95::c_int;
use ptr::{is_null, to_unsafe_ptr};
use str::as_c_str;
use vec::as_imm_buf;
use vec::raw::from_buf_raw;

pub struct Image<T> {
    width   : uint,
    height  : uint,
    depth   : uint,
    data    : ~[T],
}

pub fn new_image<T>(width: uint, height: uint, depth: uint, data: ~[T]) -> Image<T> {
    Image::<T> {
        width   : width,
        height  : height,
        depth   : depth,
        data    : move data,
    }
}

enum LoadResult {
    Error,
    ImageU8(Image<u8>),
    ImageF32(Image<f32>),
}

pub fn load(path: ~str) -> LoadResult {
    let force_depth = 0;
    load_with_depth(move path, force_depth, false)
}

priv fn load_internal<T>(buf : *T, w : c_int, h : c_int, d : c_int) -> Image<T> {
    unsafe {
        // FIXME: Shouldn't copy; instead we should use a sendable resource. They
        // aren't particularly safe yet though.
        let data = from_buf_raw(buf, (w * h * d) as uint);
        libc::free(buf as *c_void);
        Image::<T>{
            width   : w as uint,
            height  : h as uint,
            depth   : d as uint,
            data    : data}
    }
}

pub fn load_with_depth(path: ~str, force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width   = 0 as c_int;
            let mut height  = 0 as c_int;
            let mut depth   = 0 as c_int;
            as_c_str(path, |bytes| {
                if !convert_hdr && stbi_is_hdr(bytes)!=0   {
                    let buffer = stbi_loadf(
                        bytes, to_unsafe_ptr(&width), to_unsafe_ptr(&height),
                        to_unsafe_ptr(&depth), force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageF32( load_internal(buffer,width,height,depth) )
                    }
                } else {
                    let buffer = stbi_load(
                        bytes, to_unsafe_ptr(&width), to_unsafe_ptr(&height),
                        to_unsafe_ptr(&depth), force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageU8( load_internal(buffer,width,height,depth) )
                    }
                }
            })
        }
    }
}

pub fn load_from_memory(buffer: &[u8]) -> LoadResult {
    let force_depth = 0;
    load_from_memory_with_depth(buffer, force_depth, false)
}

pub fn load_from_memory_with_depth(buffer: &[u8], force_depth: uint, convert_hdr:bool) -> LoadResult {
    unsafe {
        do task::unkillable {
            let mut width = 0 as c_int;
            let mut height = 0 as c_int;
            let mut depth = 0 as c_int;
            as_imm_buf(buffer, |bytes, len| {
                if !convert_hdr && stbi_is_hdr_from_memory(bytes,len as c_int)!=0   {
                    let buffer = stbi_loadf_from_memory(
                        bytes, len as c_int, to_unsafe_ptr(&width),
                        to_unsafe_ptr(&height), to_unsafe_ptr(&depth),
                        force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageF32( load_internal(buffer,width,height,depth) )
                    }
                } else {
                    let buffer = stbi_load_from_memory(
                        bytes, len as c_int, to_unsafe_ptr(&width),
                        to_unsafe_ptr(&height), to_unsafe_ptr(&depth),
                        force_depth as c_int);
                    if is_null(buffer) {
                        Error
                    } else {
                        ImageU8( load_internal(buffer,width,height,depth) )
                    }
                }
            })
        }
    }
}
<|endoftext|>


<commit_before>use au = gfx::geometry;
use content::content_task::ContentTask;
use dom::cow;
use dom::element::*;
use dom::event::{Event, ReflowEvent};
use dom::node::{Comment, Doctype, DoctypeData, Element, Node, NodeScope, Text};
use resource::image_cache_task::ImageCacheTask;
use resource::image_cache_task;
use resource::resource_task::{Done, Load, Payload, ResourceTask};

use core::comm::{Chan, Port};
use cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
use hubbub::Attribute;
use newcss::stylesheet::Stylesheet;
use std::net::url::Url;
use std::net::url;

type JSResult = ~[~[u8]];

enum CSSMessage {
    CSSTaskNewFile(StylesheetProvenance),
    CSSTaskExit
}

enum JSMessage {
    JSTaskNewFile(Url),
    JSTaskExit
}

struct HtmlParserResult {
    root: Node,
    style_port: comm::Port<Option<Stylesheet>>,
    js_port: comm::Port<JSResult>,
}

/**
Runs a task that coordinates parsing links to css stylesheets.

This function should be spawned in a separate task and spins waiting
for the html builder to find links to css stylesheets and sends off
tasks to parse each link.  When the html process finishes, it notifies
the listener, who then collects the css rules from each task it
spawned, collates them, and sends them to the given result channel.

# Arguments

* `to_parent` - A channel on which to send back the full set of rules.
* `from_parent` - A port on which to receive new links.

*/
fn css_link_listener(to_parent : comm::Chan<Option<Stylesheet>>,
                     from_parent : comm::Port<CSSMessage>,
                     resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            CSSTaskNewFile(move provenance) => {
                result_vec.push(spawn_css_parser(move provenance, copy resource_task));
            }
            CSSTaskExit => {
                break;
            }
        }
    }

    // Send the sheets back in order
    // FIXME: Shouldn't wait until after we've recieved CSSTaskExit to start sending these
    do vec::consume(move result_vec) |_i, port| {
        to_parent.send(Some(port.recv()));
    }
    to_parent.send(None);
}

fn js_script_listener(to_parent : comm::Chan<~[~[u8]]>, from_parent : comm::Port<JSMessage>,
                      resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            JSTaskNewFile(move url) => {
                let result_port = comm::Port();
                let result_chan = comm::Chan(&result_port);
                do task::spawn |move url| {
                    let input_port = Port();
                    // TODO: change copy to move once we can move into closures
                    resource_task.send(Load(copy url, input_port.chan()));

                    let mut buf = ~[];
                    loop {
                        match input_port.recv() {
                            Payload(move data) => {
                                buf += data;
                            }
                            Done(Ok(*)) => {
                                result_chan.send(move buf);
                                break;
                            }
                            Done(Err(*)) => {
                                #error("error loading script %s", url.to_str());
                            }
                        }
                    }
                }
                vec::push(&mut result_vec, result_port);
            }
            JSTaskExit => {
                break;
            }
        }
    }

    let js_scripts = vec::map(result_vec, |result_port| result_port.recv());
    to_parent.send(move js_scripts);
}

fn build_element_kind(tag: &str) -> ~ElementKind {
    // TODO (Issue #85): use atoms
    if      tag == ~"a" { ~HTMLAnchorElement }
    else if tag == ~"aside" { ~HTMLAsideElement }
    else if tag == ~"br" { ~HTMLBRElement }
    else if tag == ~"body" { ~HTMLBodyElement }
    else if tag == ~"bold" { ~HTMLBoldElement }
    else if tag == ~"div" { ~HTMLDivElement }
    else if tag == ~"font" { ~HTMLFontElement }
    else if tag == ~"form" { ~HTMLFormElement }
    else if tag == ~"hr" { ~HTMLHRElement }
    else if tag == ~"head" { ~HTMLHeadElement }
    else if tag == ~"h1" { ~HTMLHeadingElement(Heading1) }
    else if tag == ~"h2" { ~HTMLHeadingElement(Heading2) }
    else if tag == ~"h3" { ~HTMLHeadingElement(Heading3) }
    else if tag == ~"h4" { ~HTMLHeadingElement(Heading4) }
    else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) }
    else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) }
    else if tag == ~"html" { ~HTMLHtmlElement }
    else if tag == ~"img" { ~HTMLImageElement(HTMLImageData()) }
    else if tag == ~"input" { ~HTMLInputElement }
    else if tag == ~"i" { ~HTMLItalicElement }
    else if tag == ~"link" { ~HTMLLinkElement }
    else if tag == ~"li" { ~HTMLListItemElement }
    else if tag == ~"meta" { ~HTMLMetaElement }
    else if tag == ~"ol" { ~HTMLOListElement }
    else if tag == ~"option" { ~HTMLOptionElement }
    else if tag == ~"p" { ~HTMLParagraphElement }
    else if tag == ~"script" { ~HTMLScriptElement }
    else if tag == ~"section" { ~HTMLSectionElement }
    else if tag == ~"select" { ~HTMLSelectElement }
    else if tag == ~"small" { ~HTMLSmallElement }
    else if tag == ~"span" { ~HTMLSpanElement }
    else if tag == ~"style" { ~HTMLStyleElement }
    else if tag == ~"tbody" { ~HTMLTableBodyElement }
    else if tag == ~"td" { ~HTMLTableCellElement }
    else if tag == ~"table" { ~HTMLTableElement }
    else if tag == ~"tr" { ~HTMLTableRowElement }
    else if tag == ~"title" { ~HTMLTitleElement }
    else if tag == ~"ul" { ~HTMLUListElement }
    else { ~UnknownElement }
}

pub fn parse_html(scope: NodeScope,
                  url: Url,
                  resource_task: ResourceTask,
                  image_cache_task: ImageCacheTask) -> HtmlParserResult unsafe {
    // Spawn a CSS parser to receive links to CSS style sheets.
    let (css_port, css_chan): (comm::Port<Option<Stylesheet>>, comm::Chan<CSSMessage>) =
            do task::spawn_conversation |css_port: comm::Port<CSSMessage>,
                                         css_chan: comm::Chan<Option<Stylesheet>>| {
        css_link_listener(css_chan, css_port, resource_task);
    };

    // Spawn a JS parser to receive JavaScript.
    let (js_port, js_chan): (comm::Port<JSResult>, comm::Chan<JSMessage>) =
            do task::spawn_conversation |js_port: comm::Port<JSMessage>,
                                         js_chan: comm::Chan<JSResult>| {
        js_script_listener(js_chan, js_port, resource_task);
    };

    let (scope, url) = (@copy scope, @move url);

    // Build the root node.
    let root = scope.new_node(Element(ElementData(~"html", ~HTMLDivElement)));
    debug!("created new node");
    let parser = hubbub::Parser("UTF-8", false);
    debug!("created parser");
    parser.set_document_node(cast::transmute(cow::unwrap(root)));
    parser.enable_scripting(true);

    // Performs various actions necessary after appending has taken place. Currently, this consists
    // of processing inline stylesheets, but in the future it might perform prefetching, etc.
    let append_hook: @fn(Node, Node) = |parent_node, child_node| {
        do scope.read(&parent_node) |parent_node_contents| {
            do scope.read(&child_node) |child_node_contents| {
                match (parent_node_contents.kind, child_node_contents.kind) {
                    (~Element(ref element), ~Text(ref data)) => {
                        match element.kind {
                            ~HTMLStyleElement => {
                                debug!("found inline CSS stylesheet");
                                let url = url::from_str("http://example.com/"); // FIXME
                                let provenance = InlineProvenance(result::unwrap(move url),
                                                                  copy *data);
                                css_chan.send(CSSTaskNewFile(provenance));
                            }
                            _ => {} // Nothing to do.
                        }
                    }
                    _ => {} // Nothing to do.
                }
            }
        }
    };

    parser.set_tree_handler(@hubbub::TreeHandler {
        create_comment: |data: ~str| {
            debug!("create comment");
            let new_node = scope.new_node(Comment(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        create_doctype: |doctype: ~hubbub::Doctype| {
            debug!("create doctype");
            // TODO: remove copying here by using struct pattern matching to
            // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
            let public_id = match doctype.public_id {
                None => None,
                Some(id) => Some(copy id)
            };
            let system_id = match doctype.system_id {
                None => None,
                Some(id) => Some(copy id)
            };
            let data = DoctypeData(copy doctype.name, move public_id, move system_id,
                                   copy doctype.force_quirks);
            let new_node = scope.new_node(Doctype(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        create_element: |tag: ~hubbub::Tag, move image_cache_task| {
            debug!("create element");
            // TODO: remove copying here by using struct pattern matching to
            // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
            let elem_kind = build_element_kind(tag.name);
            let elem = ElementData(copy tag.name, move elem_kind);

            debug!("attach attrs");
            for tag.attributes.each |attr| {
                elem.attrs.push(~Attr(copy attr.name, copy attr.value));
            }

            // Spawn additional parsing, network loads, etc. from tag and attrs
            match elem.kind {
                //Handle CSS style sheets from <link> elements
                ~HTMLLinkElement => {
                    match (elem.get_attr(~"rel"), elem.get_attr(~"href")) {
                        (Some(move rel), Some(move href)) => {
                            if rel == ~"stylesheet" {
                                debug!("found CSS stylesheet: %s", href);
                                css_chan.send(CSSTaskNewFile(UrlProvenance(make_url(move href,
                                                                           Some(copy *url)))));
                            }
                        }
                        _ => {}
                    }
                },
                ~HTMLImageElement(ref d) => {
                    do elem.get_attr(~"src").iter |img_url_str| {
                        let img_url = make_url(copy *img_url_str, Some(copy *url));
                        d.image = Some(copy img_url);
                        // inform the image cache to load this, but don't store a handle.
                        // TODO (Issue #84): don't prefetch if we are within a <noscript> tag.
                        image_cache_task.send(image_cache_task::Prefetch(move img_url));
                    }
                }
                //TODO (Issue #86): handle inline styles ('style' attr)
                _ => {}
            }
            let node = scope.new_node(Element(move elem));
            unsafe { cast::transmute(cow::unwrap(node)) }
        },
        create_text: |data: ~str| {
            debug!("create text");
            let new_node = scope.new_node(Text(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        ref_node: |_node| {},
        unref_node: |_node| {},
        append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| unsafe {
            debug!("append child");
            unsafe {
                let p: Node = cow::wrap(cast::transmute(parent));
                let c: Node = cow::wrap(cast::transmute(child));
                scope.add_child(p, c);
                append_hook(p, c);
            }
            child
        },
        insert_before: |_parent, _child| {
            debug!("insert before");
            0u
        },
        remove_child: |_parent, _child| {
            debug!("remove child");
            0u
        },
        clone_node: |_node, _deep| {
            debug!("clone node");
            0u
        },
        reparent_children: |_node, _new_parent| {
            debug!("reparent children");
            0u
        },
        get_parent: |_node, _element_only| {
            debug!("get parent");
            0u
        },
        has_children: |_node| {
            debug!("has children");
            false
        },
        form_associate: |_form, _node| {
            debug!("form associate");
        },
        add_attributes: |_node, _attributes| {
            debug!("add attributes");
        },
        set_quirks_mode: |_mode| {
            debug!("set quirks mode");
        },
        encoding_change: |_encname| {
            debug!("encoding change");
        },
        complete_script: |script| {
            // A little function for holding this lint attr
            #[allow(non_implicitly_copyable_typarams)]
            fn complete_script(scope: &NodeScope, script: hubbub::NodeDataPtr, url: &Url, js_chan: &comm::Chan<JSMessage>) unsafe {
                do scope.read(&cow::wrap(cast::transmute(script))) |node_contents| {
                    match *node_contents.kind {
                        Element(element) if element.tag_name == ~"script" => {
                            match element.get_attr(~"src") {
                                Some(move src) => {
                                    debug!("found script: %s", src);
                                    let new_url = make_url(move src, Some(copy *url));
                                    js_chan.send(JSTaskNewFile(move new_url));
                                }
                                None => {}
                            }
                        }
                        _ => {}
                    }
                }
            }
            complete_script(scope, script, url, &js_chan);
            debug!("complete script");
        }
    });
    debug!("set tree handler");

    let input_port = Port();
    resource_task.send(Load(copy *url, input_port.chan()));
    debug!("loaded page");
    loop {
        match input_port.recv() {
            Payload(data) => {
                debug!("received data");
                parser.parse_chunk(data);
            }
            Done(*) => {
                break;
            }
        }
    }

    css_chan.send(CSSTaskExit);
    js_chan.send(JSTaskExit);

    return HtmlParserResult { root: root, style_port: css_port, js_port: js_port };
}

<commit_msg>html: Implement shallow node cloning. http://yellow5.com/pokey/ doesn't crash now.<commit_after>use au = gfx::geometry;
use content::content_task::ContentTask;
use dom::cow;
use dom::element::*;
use dom::event::{Event, ReflowEvent};
use dom::node::{Comment, Doctype, DoctypeData, Element, Node, NodeScope, Text};
use resource::image_cache_task::ImageCacheTask;
use resource::image_cache_task;
use resource::resource_task::{Done, Load, Payload, ResourceTask};

use core::comm::{Chan, Port};
use cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
use hubbub::Attribute;
use newcss::stylesheet::Stylesheet;
use std::net::url::Url;
use std::net::url;

type JSResult = ~[~[u8]];

enum CSSMessage {
    CSSTaskNewFile(StylesheetProvenance),
    CSSTaskExit
}

enum JSMessage {
    JSTaskNewFile(Url),
    JSTaskExit
}

struct HtmlParserResult {
    root: Node,
    style_port: comm::Port<Option<Stylesheet>>,
    js_port: comm::Port<JSResult>,
}

/**
Runs a task that coordinates parsing links to css stylesheets.

This function should be spawned in a separate task and spins waiting
for the html builder to find links to css stylesheets and sends off
tasks to parse each link.  When the html process finishes, it notifies
the listener, who then collects the css rules from each task it
spawned, collates them, and sends them to the given result channel.

# Arguments

* `to_parent` - A channel on which to send back the full set of rules.
* `from_parent` - A port on which to receive new links.

*/
fn css_link_listener(to_parent : comm::Chan<Option<Stylesheet>>,
                     from_parent : comm::Port<CSSMessage>,
                     resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            CSSTaskNewFile(move provenance) => {
                result_vec.push(spawn_css_parser(move provenance, copy resource_task));
            }
            CSSTaskExit => {
                break;
            }
        }
    }

    // Send the sheets back in order
    // FIXME: Shouldn't wait until after we've recieved CSSTaskExit to start sending these
    do vec::consume(move result_vec) |_i, port| {
        to_parent.send(Some(port.recv()));
    }
    to_parent.send(None);
}

fn js_script_listener(to_parent : comm::Chan<~[~[u8]]>, from_parent : comm::Port<JSMessage>,
                      resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            JSTaskNewFile(move url) => {
                let result_port = comm::Port();
                let result_chan = comm::Chan(&result_port);
                do task::spawn |move url| {
                    let input_port = Port();
                    // TODO: change copy to move once we can move into closures
                    resource_task.send(Load(copy url, input_port.chan()));

                    let mut buf = ~[];
                    loop {
                        match input_port.recv() {
                            Payload(move data) => {
                                buf += data;
                            }
                            Done(Ok(*)) => {
                                result_chan.send(move buf);
                                break;
                            }
                            Done(Err(*)) => {
                                #error("error loading script %s", url.to_str());
                            }
                        }
                    }
                }
                vec::push(&mut result_vec, result_port);
            }
            JSTaskExit => {
                break;
            }
        }
    }

    let js_scripts = vec::map(result_vec, |result_port| result_port.recv());
    to_parent.send(move js_scripts);
}

fn build_element_kind(tag: &str) -> ~ElementKind {
    // TODO (Issue #85): use atoms
    if      tag == ~"a" { ~HTMLAnchorElement }
    else if tag == ~"aside" { ~HTMLAsideElement }
    else if tag == ~"br" { ~HTMLBRElement }
    else if tag == ~"body" { ~HTMLBodyElement }
    else if tag == ~"bold" { ~HTMLBoldElement }
    else if tag == ~"div" { ~HTMLDivElement }
    else if tag == ~"font" { ~HTMLFontElement }
    else if tag == ~"form" { ~HTMLFormElement }
    else if tag == ~"hr" { ~HTMLHRElement }
    else if tag == ~"head" { ~HTMLHeadElement }
    else if tag == ~"h1" { ~HTMLHeadingElement(Heading1) }
    else if tag == ~"h2" { ~HTMLHeadingElement(Heading2) }
    else if tag == ~"h3" { ~HTMLHeadingElement(Heading3) }
    else if tag == ~"h4" { ~HTMLHeadingElement(Heading4) }
    else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) }
    else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) }
    else if tag == ~"html" { ~HTMLHtmlElement }
    else if tag == ~"img" { ~HTMLImageElement(HTMLImageData()) }
    else if tag == ~"input" { ~HTMLInputElement }
    else if tag == ~"i" { ~HTMLItalicElement }
    else if tag == ~"link" { ~HTMLLinkElement }
    else if tag == ~"li" { ~HTMLListItemElement }
    else if tag == ~"meta" { ~HTMLMetaElement }
    else if tag == ~"ol" { ~HTMLOListElement }
    else if tag == ~"option" { ~HTMLOptionElement }
    else if tag == ~"p" { ~HTMLParagraphElement }
    else if tag == ~"script" { ~HTMLScriptElement }
    else if tag == ~"section" { ~HTMLSectionElement }
    else if tag == ~"select" { ~HTMLSelectElement }
    else if tag == ~"small" { ~HTMLSmallElement }
    else if tag == ~"span" { ~HTMLSpanElement }
    else if tag == ~"style" { ~HTMLStyleElement }
    else if tag == ~"tbody" { ~HTMLTableBodyElement }
    else if tag == ~"td" { ~HTMLTableCellElement }
    else if tag == ~"table" { ~HTMLTableElement }
    else if tag == ~"tr" { ~HTMLTableRowElement }
    else if tag == ~"title" { ~HTMLTitleElement }
    else if tag == ~"ul" { ~HTMLUListElement }
    else { ~UnknownElement }
}

pub fn parse_html(scope: NodeScope,
                  url: Url,
                  resource_task: ResourceTask,
                  image_cache_task: ImageCacheTask) -> HtmlParserResult unsafe {
    // Spawn a CSS parser to receive links to CSS style sheets.
    let (css_port, css_chan): (comm::Port<Option<Stylesheet>>, comm::Chan<CSSMessage>) =
            do task::spawn_conversation |css_port: comm::Port<CSSMessage>,
                                         css_chan: comm::Chan<Option<Stylesheet>>| {
        css_link_listener(css_chan, css_port, resource_task);
    };

    // Spawn a JS parser to receive JavaScript.
    let (js_port, js_chan): (comm::Port<JSResult>, comm::Chan<JSMessage>) =
            do task::spawn_conversation |js_port: comm::Port<JSMessage>,
                                         js_chan: comm::Chan<JSResult>| {
        js_script_listener(js_chan, js_port, resource_task);
    };

    let (scope, url) = (@copy scope, @move url);

    // Build the root node.
    let root = scope.new_node(Element(ElementData(~"html", ~HTMLDivElement)));
    debug!("created new node");
    let parser = hubbub::Parser("UTF-8", false);
    debug!("created parser");
    parser.set_document_node(cast::transmute(cow::unwrap(root)));
    parser.enable_scripting(true);

    // Performs various actions necessary after appending has taken place. Currently, this consists
    // of processing inline stylesheets, but in the future it might perform prefetching, etc.
    let append_hook: @fn(Node, Node) = |parent_node, child_node| {
        do scope.read(&parent_node) |parent_node_contents| {
            do scope.read(&child_node) |child_node_contents| {
                match (parent_node_contents.kind, child_node_contents.kind) {
                    (~Element(ref element), ~Text(ref data)) => {
                        match element.kind {
                            ~HTMLStyleElement => {
                                debug!("found inline CSS stylesheet");
                                let url = url::from_str("http://example.com/"); // FIXME
                                let provenance = InlineProvenance(result::unwrap(move url),
                                                                  copy *data);
                                css_chan.send(CSSTaskNewFile(provenance));
                            }
                            _ => {} // Nothing to do.
                        }
                    }
                    _ => {} // Nothing to do.
                }
            }
        }
    };

    parser.set_tree_handler(@hubbub::TreeHandler {
        create_comment: |data: ~str| {
            debug!("create comment");
            let new_node = scope.new_node(Comment(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        create_doctype: |doctype: ~hubbub::Doctype| {
            debug!("create doctype");
            // TODO: remove copying here by using struct pattern matching to
            // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
            let public_id = match doctype.public_id {
                None => None,
                Some(id) => Some(copy id)
            };
            let system_id = match doctype.system_id {
                None => None,
                Some(id) => Some(copy id)
            };
            let data = DoctypeData(copy doctype.name, move public_id, move system_id,
                                   copy doctype.force_quirks);
            let new_node = scope.new_node(Doctype(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        create_element: |tag: ~hubbub::Tag, move image_cache_task| {
            debug!("create element");
            // TODO: remove copying here by using struct pattern matching to
            // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
            let elem_kind = build_element_kind(tag.name);
            let elem = ElementData(copy tag.name, move elem_kind);

            debug!("-- attach attrs");
            for tag.attributes.each |attr| {
                elem.attrs.push(~Attr(copy attr.name, copy attr.value));
            }

            // Spawn additional parsing, network loads, etc. from tag and attrs
            match elem.kind {
                //Handle CSS style sheets from <link> elements
                ~HTMLLinkElement => {
                    match (elem.get_attr(~"rel"), elem.get_attr(~"href")) {
                        (Some(move rel), Some(move href)) => {
                            if rel == ~"stylesheet" {
                                debug!("found CSS stylesheet: %s", href);
                                css_chan.send(CSSTaskNewFile(UrlProvenance(make_url(move href,
                                                                           Some(copy *url)))));
                            }
                        }
                        _ => {}
                    }
                },
                ~HTMLImageElement(ref d) => {
                    do elem.get_attr(~"src").iter |img_url_str| {
                        let img_url = make_url(copy *img_url_str, Some(copy *url));
                        d.image = Some(copy img_url);
                        // inform the image cache to load this, but don't store a handle.
                        // TODO (Issue #84): don't prefetch if we are within a <noscript> tag.
                        image_cache_task.send(image_cache_task::Prefetch(move img_url));
                    }
                }
                //TODO (Issue #86): handle inline styles ('style' attr)
                _ => {}
            }
            let node = scope.new_node(Element(move elem));
            unsafe { cast::transmute(cow::unwrap(node)) }
        },
        create_text: |data: ~str| {
            debug!("create text");
            let new_node = scope.new_node(Text(move data));
            unsafe { cast::transmute(cow::unwrap(new_node)) }
        },
        ref_node: |_node| {},
        unref_node: |_node| {},
        append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| unsafe {
            debug!("append child %x %x", cast::transmute(parent), cast::transmute(child));
            unsafe {
                let p: Node = cow::wrap(cast::transmute(parent));
                let c: Node = cow::wrap(cast::transmute(child));
                scope.add_child(p, c);
                append_hook(p, c);
            }
            child
        },
        insert_before: |_parent, _child| {
            debug!("insert before");
            0u
        },
        remove_child: |_parent, _child| {
            debug!("remove child");
            0u
        },
        clone_node: |node, deep| {
            debug!("clone node");
            unsafe {
                if deep { error!("-- deep clone unimplemented"); }
                let n: Node = cow::wrap(cast::transmute(node));
                let data = n.read(|read_data| copy *read_data.kind);
                let new_node = scope.new_node(move data);
                unsafe { cast::transmute(cow::unwrap(new_node)) }
            }
        },
        reparent_children: |_node, _new_parent| {
            debug!("reparent children");
            0u
        },
        get_parent: |_node, _element_only| {
            debug!("get parent");
            0u
        },
        has_children: |_node| {
            debug!("has children");
            false
        },
        form_associate: |_form, _node| {
            debug!("form associate");
        },
        add_attributes: |_node, _attributes| {
            debug!("add attributes");
        },
        set_quirks_mode: |_mode| {
            debug!("set quirks mode");
        },
        encoding_change: |_encname| {
            debug!("encoding change");
        },
        complete_script: |script| {
            // A little function for holding this lint attr
            #[allow(non_implicitly_copyable_typarams)]
            fn complete_script(scope: &NodeScope, script: hubbub::NodeDataPtr, url: &Url, js_chan: &comm::Chan<JSMessage>) unsafe {
                do scope.read(&cow::wrap(cast::transmute(script))) |node_contents| {
                    match *node_contents.kind {
                        Element(element) if element.tag_name == ~"script" => {
                            match element.get_attr(~"src") {
                                Some(move src) => {
                                    debug!("found script: %s", src);
                                    let new_url = make_url(move src, Some(copy *url));
                                    js_chan.send(JSTaskNewFile(move new_url));
                                }
                                None => {}
                            }
                        }
                        _ => {}
                    }
                }
            }
            complete_script(scope, script, url, &js_chan);
            debug!("complete script");
        }
    });
    debug!("set tree handler");

    let input_port = Port();
    resource_task.send(Load(copy *url, input_port.chan()));
    debug!("loaded page");
    loop {
        match input_port.recv() {
            Payload(data) => {
                debug!("received data");
                parser.parse_chunk(data);
            }
            Done(*) => {
                break;
            }
        }
    }

    css_chan.send(CSSTaskExit);
    js_chan.send(JSTaskExit);

    return HtmlParserResult { root: root, style_port: css_port, js_port: js_port };
}

<|endoftext|>


<commit_before>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

use std::collections::{HashSet, VecMap};
use std::num::Int;

use constants::{BLOCK_SIZE, LINE_SIZE, NUM_LINES_PER_BLOCK};
use gc_object::GCObjectRef;

/// A `BlockInfo` contains management information for the immix garbage
/// collectors.
///
/// It is the first bytes in a chunk of memory of size `BLOCK_SIZE` and must
/// not exeed `LINE_SIZE` bytes in size.
pub struct BlockInfo {
    /// A counter of live objects for every line in this block.
    line_counter: VecMap<u8>,

    /// A set of addresses that are valid objects. Needed for the conservative
    /// part.
    object_map: HashSet<GCObjectRef>,

    /// Objects in this block that were never touched by the garbage
    /// collector.
    new_objects: HashSet<GCObjectRef>,

    /// If this block is actually in use.
    allocated: bool,

    /// How many holen are in this block.
    hole_count: u8,

    /// If this block is a candidate for opportunistic evacuation.
    evacuation_candidate: bool,
}

impl BlockInfo {
    /// Create a new `BlockInfo`.
    pub fn new() -> BlockInfo {
        let mut line_counter = VecMap::with_capacity(NUM_LINES_PER_BLOCK);
        for index in (0..NUM_LINES_PER_BLOCK) {
            line_counter.insert(index, 0);
        }
        return BlockInfo {
            line_counter: line_counter,
            object_map: HashSet::new(),
            new_objects: HashSet::new(),
            allocated: false,
            hole_count: 0,
            evacuation_candidate: false,
        };
    }

    /// Set this block as allocated (actually in use).
    pub fn set_allocated(&mut self) {
        self.allocated = true;
    }

    /// Set an address in this block as a valid object.
    pub fn set_gc_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "set_gc_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.object_map.insert(object);
    }

    /// Unset an address in this block as a valid object.
    pub fn unset_gc_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "unset_gc_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.object_map.remove(&object);
    }

    /// Return if an address in this block is a valid object.
    pub fn is_gc_object(&self, object: GCObjectRef) -> bool {
        if self.is_in_block(object) {
            return self.object_map.contains(&object);
        }
        return false;
    }

    /// Get a copy of the object map.
    pub fn get_object_map(&mut self) -> HashSet<GCObjectRef> {
        return self.object_map.clone();
    }

    /// Clear the object map.
    pub fn clear_object_map(&mut self) {
        self.object_map.clear();
    }

    /// Set an object in this block as new (not the `GCHeader.new` bit).
    pub fn set_new_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "set_new_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.new_objects.insert(object);
    }

    /// Get the new objects in this block.
    pub fn get_new_objects(&mut self) -> HashSet<GCObjectRef> {
        return self.new_objects.clone();
    }

    /// Remove all the new objects from the object map and clear the new
    /// objects set.
    pub fn remove_new_objects_from_map(&mut self) {
        let new_objects = self.new_objects.drain().collect();
        let difference = self.object_map.difference(&new_objects)
                                        .map(|o| *o).collect();
        self.object_map = difference;
    }

    /// Set as an evacuation candidate if this block has at least `hole_count`
    /// holes.
    pub fn set_evacuation_candidate(&mut self, hole_count: u8) {
        debug!("Set block {:p} to evacuation_candidate={} ({} holes)",
               &self, self.hole_count >= hole_count, self.hole_count);
        self.evacuation_candidate = self.hole_count >= hole_count;
    }

    /// Return if this is an evacuation candidate.
    pub fn is_evacuation_candidate(&self) -> bool{
        return self.evacuation_candidate;
    }

    /// Increment the lines on which the object is allocated.
    pub fn increment_lines(&mut self, object: GCObjectRef) {
        self.update_line_nums(object, true);
    }

    /// Decrement the lines on which the object is allocated.
    pub fn decrement_lines(&mut self, object: GCObjectRef) {
        self.update_line_nums(object, false);
    }

    /// Return the number of holes and marked lines in this block.
    ///
    /// A marked line is a line with a count of at least one.
    ///
    /// _Note_: You must call count_holes() bevorhand to set the number of
    /// holes.
    pub fn count_holes_and_marked_lines(&self) -> (u8, u8) {
        return (self.hole_count,
                self.line_counter.values().filter(|&e| *e != 0).count() as u8);
    }

    /// Return the number of holes and available lines in this block.
    ///
    /// An available line is a line with a count of zero.
    ///
    /// _Note_: You must call count_holes() bevorhand to set the number of
    /// holes.
    pub fn count_holes_and_available_lines(&self) -> (u8, u8) {
        return (self.hole_count,
                self.line_counter.values().filter(|&e| *e == 0).count() as u8);
    }

    /// Clear the line counter map.
    pub fn clear_line_counts(&mut self) {
        for index in (0..NUM_LINES_PER_BLOCK) {
            self.line_counter.insert(index, 0);
        }
    }

    /// Reset all member field for this block.
    pub fn reset(&mut self) {
        self.clear_line_counts();
        self.clear_object_map();
        self.allocated = false;
        self.hole_count = 0;
        self.evacuation_candidate = false;
    }

    /// Return true if no line is marked (every line has a count of zero).
    pub fn is_empty(&self) -> bool {
        return self.line_counter.values().all(|v| *v == 0);
    }

    /// Get a pointer to an address `offset` bytes into this block.
    pub fn offset(&mut self, offset: usize) -> GCObjectRef {
        let self_ptr = self as *mut BlockInfo;
        let object = unsafe { (self_ptr as *mut u8).offset(offset as isize) };
        return object as GCObjectRef;
    }

    /// Scan the block for a hole to allocate into.
    ///
    /// The scan will start at `last_high_offset` bytes into the block and
    /// return a tuple of `low_offset`, `high_offset` as the lowest and
    /// highest usable offsets for a hole.
    ///
    /// `None` is returned if no hole was found.
    pub fn scan_block(&self, last_high_offset: u16) -> Option<(u16, u16)> {
        let last_high_index = last_high_offset as usize / LINE_SIZE;
        debug!("Scanning block {:p} for a hole with last_high_offset {}",
               self, last_high_index);
        let mut low_index = NUM_LINES_PER_BLOCK - 1;
        for index in ((last_high_index + 1)..NUM_LINES_PER_BLOCK) {
            if self.line_counter.get(&index).map_or(true, |c| *c == 0) {
                // +1 to skip the next line in case an object straddles lines
                low_index = index + 1;
                break;
            }
        }
        let mut high_index = NUM_LINES_PER_BLOCK;
        for index in (low_index..NUM_LINES_PER_BLOCK) {
            if self.line_counter.get(&index).map_or(false, |c| *c != 0) {
                high_index = index;
                break;
            }
        }
        if low_index == high_index && high_index != (NUM_LINES_PER_BLOCK - 1) {
            debug!("Rescan: Found single line hole? in block {:p}", self);
            return self.scan_block((high_index * LINE_SIZE - 1) as u16);
        } else if low_index < (NUM_LINES_PER_BLOCK - 1) {
            debug!("Found low index {} and high index {} in block {:p}",
                   low_index, high_index, self);
            return Some(((low_index * LINE_SIZE) as u16,
                         (high_index * LINE_SIZE - 1) as u16));
        }
        debug!("Found no hole in block {:p}", self);
        return None;
    }

    /// Count the holes in this block.
    ///
    /// Holes are lines with no objects allocated.
    pub fn count_holes(&mut self) {
        let holes = self.line_counter.values()
            .fold((0, false), |(holes, in_hole), &elem|
                  match (in_hole, elem) {
                    (false, 0) => (holes + 1, true),
                    (_, _) => (holes, false),
                  }).0;
        self.hole_count = holes;
    }
}

impl BlockInfo{
    /// Returns true if this block is allocated and the address is within the
    /// bounds of this block.
    fn is_in_block(&self, object: GCObjectRef) -> bool {
        // This works because we get zeroed memory from the OS, so
        // self.allocated will be false if this block is not initialized and
        // this method gets only called for objects within the ImmixSpace.
        // After the first initialization the field is properly managed.
        if self.allocated {
            let self_ptr = self as *const BlockInfo as *const u8;
            let self_bound = unsafe{ self_ptr.offset(BLOCK_SIZE as isize)};
            return self_ptr < (object as *const u8)
                && (object as *const u8) <  self_bound;
        }
        return false;
    }

    /// Convert an address on this block into a line number.
    fn object_to_line_num(object: GCObjectRef) -> usize {
        return (object as usize % BLOCK_SIZE) / LINE_SIZE;
    }

    /// Update the line counter for the given object.
    ///
    /// Increment if `increment`, otherwise do a saturating substraction.
    fn update_line_nums(&mut self, object: GCObjectRef, increment: bool) {
        // This calculates how many lines are affected starting from a
        // LINE_SIZE aligned address. So it might not mark enough lines. But
        // that does not matter as we always skip a line in scan_block()
        let line_num = BlockInfo::object_to_line_num(object);
        let object_size = unsafe{ (*object).object_size() };
        for line in line_num..(line_num + (object_size / LINE_SIZE) + 1) {
            match increment {
                true => {
                    if self.line_counter.contains_key(&line) {
                        if let Some(val) = self.line_counter.get_mut(&line) {
                            *val += 1;
                        }
                    } else { self.line_counter.insert(line, 1); }
                    debug!("Incremented line count for line {} to {}", line,
                           self.line_counter.get(&line).unwrap());
                },
                false => {
                    if self.line_counter.contains_key(&line) {
                        if let Some(val) = self.line_counter.get_mut(&line) {
                            *val = Int::saturating_sub(*val, 1);
                        }
                    } else { self.line_counter.insert(line, 0); }
                    debug!("Decremented line count for line {} to {}", line,
                           self.line_counter.get(&line).unwrap());
                }
            }
        }
    }
}
<commit_msg>Change the object_map in BlockInfo to a BitvSet (perf improv)<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

use std::collections::{BitvSet, HashSet, VecMap};
use std::num::Int;

use constants::{BLOCK_SIZE, LINE_SIZE, NUM_LINES_PER_BLOCK};
use gc_object::GCObjectRef;

/// A per block object map.
struct ObjectMap {
    set: BitvSet,
}

impl ObjectMap {
    /// Create a new `ObjectMap`.
    fn new() -> ObjectMap {
        return ObjectMap {
            set: BitvSet::with_capacity(BLOCK_SIZE),
        };
    }

    /// Reduce the objects address to an offset within the block.
    fn index(object: GCObjectRef) -> usize {
        return (object as usize) % BLOCK_SIZE;
    }

    /// Set the address as a valid object.
    fn set_object(&mut self, object: GCObjectRef) {
        self.set.insert(ObjectMap::index(object));
    }

    /// Unset the address as a valid object.
    fn unset_object(&mut self, object: GCObjectRef) {
        self.set.remove(&ObjectMap::index(object));
    }

    /// Return `true` is the address is a valid object.
    fn is_object(&self, object: GCObjectRef) -> bool {
        return self.set.contains(&ObjectMap::index(object));
    }

    /// Update this `ObjectMap` with the difference of this `ObjectMap` and
    /// the other.
    fn difference(&mut self, other: &ObjectMap) {
        self.set.difference_with(&other.set);
    }

    /// Clear all entries.
    fn clear(&mut self) {
        self.set.clear();
    }

    /// Retrieve the values as a `HashSet`.
    fn as_hashset(&self, base: *mut u8) -> HashSet<GCObjectRef> {
        return self.set.iter()
                       .map(|i| unsafe{ base.offset(i as isize) as GCObjectRef})
                       .collect();
    }
}

/// A `BlockInfo` contains management information for the immix garbage
/// collectors.
///
/// It is the first bytes in a chunk of memory of size `BLOCK_SIZE` and must
/// not exeed `LINE_SIZE` bytes in size.
pub struct BlockInfo {
    /// A counter of live objects for every line in this block.
    line_counter: VecMap<u8>,

    /// A set of addresses that are valid objects. Needed for the conservative
    /// part.
    object_map: ObjectMap,

    /// Objects in this block that were never touched by the garbage
    /// collector.
    new_objects: ObjectMap,

    /// If this block is actually in use.
    allocated: bool,

    /// How many holen are in this block.
    hole_count: u8,

    /// If this block is a candidate for opportunistic evacuation.
    evacuation_candidate: bool,
}

impl BlockInfo {
    /// Create a new `BlockInfo`.
    pub fn new() -> BlockInfo {
        let mut line_counter = VecMap::with_capacity(NUM_LINES_PER_BLOCK);
        for index in (0..NUM_LINES_PER_BLOCK) {
            line_counter.insert(index, 0);
        }
        return BlockInfo {
            line_counter: line_counter,
            object_map: ObjectMap::new(),
            new_objects: ObjectMap::new(),
            allocated: false,
            hole_count: 0,
            evacuation_candidate: false,
        };
    }

    /// Set this block as allocated (actually in use).
    pub fn set_allocated(&mut self) {
        self.allocated = true;
    }

    /// Set an address in this block as a valid object.
    pub fn set_gc_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "set_gc_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.object_map.set_object(object);
    }

    /// Unset an address in this block as a valid object.
    pub fn unset_gc_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "unset_gc_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.object_map.unset_object(object);
    }

    /// Return if an address in this block is a valid object.
    pub fn is_gc_object(&self, object: GCObjectRef) -> bool {
        if self.is_in_block(object) {
            return self.object_map.is_object(object);
        }
        return false;
    }

    /// Get a copy of the object map.
    pub fn get_object_map(&mut self) -> HashSet<GCObjectRef> {
        return self.object_map.as_hashset(self as *mut BlockInfo as *mut u8);
    }

    /// Clear the object map.
    pub fn clear_object_map(&mut self) {
        self.object_map.clear();
    }

    /// Set an object in this block as new (not the `GCHeader.new` bit).
    pub fn set_new_object(&mut self, object: GCObjectRef) {
        debug_assert!(self.is_in_block(object),
            "set_new_object() on invalid block: {:p} (allocated={})",
            self, self.allocated);
        self.new_objects.set_object(object);
    }

    /// Get the new objects in this block.
    pub fn get_new_objects(&mut self) -> HashSet<GCObjectRef> {
        return self.new_objects.as_hashset(self as *mut BlockInfo as *mut u8);
    }

    /// Remove all the new objects from the object map and clear the new
    /// objects set.
    pub fn remove_new_objects_from_map(&mut self) {
        self.object_map.difference(&self.new_objects);
        self.new_objects.clear();
    }

    /// Set as an evacuation candidate if this block has at least `hole_count`
    /// holes.
    pub fn set_evacuation_candidate(&mut self, hole_count: u8) {
        debug!("Set block {:p} to evacuation_candidate={} ({} holes)",
               &self, self.hole_count >= hole_count, self.hole_count);
        self.evacuation_candidate = self.hole_count >= hole_count;
    }

    /// Return if this is an evacuation candidate.
    pub fn is_evacuation_candidate(&self) -> bool{
        return self.evacuation_candidate;
    }

    /// Increment the lines on which the object is allocated.
    pub fn increment_lines(&mut self, object: GCObjectRef) {
        self.update_line_nums(object, true);
    }

    /// Decrement the lines on which the object is allocated.
    pub fn decrement_lines(&mut self, object: GCObjectRef) {
        self.update_line_nums(object, false);
    }

    /// Return the number of holes and marked lines in this block.
    ///
    /// A marked line is a line with a count of at least one.
    ///
    /// _Note_: You must call count_holes() bevorhand to set the number of
    /// holes.
    pub fn count_holes_and_marked_lines(&self) -> (u8, u8) {
        return (self.hole_count,
                self.line_counter.values().filter(|&e| *e != 0).count() as u8);
    }

    /// Return the number of holes and available lines in this block.
    ///
    /// An available line is a line with a count of zero.
    ///
    /// _Note_: You must call count_holes() bevorhand to set the number of
    /// holes.
    pub fn count_holes_and_available_lines(&self) -> (u8, u8) {
        return (self.hole_count,
                self.line_counter.values().filter(|&e| *e == 0).count() as u8);
    }

    /// Clear the line counter map.
    pub fn clear_line_counts(&mut self) {
        for index in (0..NUM_LINES_PER_BLOCK) {
            self.line_counter.insert(index, 0);
        }
    }

    /// Reset all member field for this block.
    pub fn reset(&mut self) {
        self.clear_line_counts();
        self.clear_object_map();
        self.allocated = false;
        self.hole_count = 0;
        self.evacuation_candidate = false;
    }

    /// Return true if no line is marked (every line has a count of zero).
    pub fn is_empty(&self) -> bool {
        return self.line_counter.values().all(|v| *v == 0);
    }

    /// Get a pointer to an address `offset` bytes into this block.
    pub fn offset(&mut self, offset: usize) -> GCObjectRef {
        let self_ptr = self as *mut BlockInfo;
        let object = unsafe { (self_ptr as *mut u8).offset(offset as isize) };
        return object as GCObjectRef;
    }

    /// Scan the block for a hole to allocate into.
    ///
    /// The scan will start at `last_high_offset` bytes into the block and
    /// return a tuple of `low_offset`, `high_offset` as the lowest and
    /// highest usable offsets for a hole.
    ///
    /// `None` is returned if no hole was found.
    pub fn scan_block(&self, last_high_offset: u16) -> Option<(u16, u16)> {
        let last_high_index = last_high_offset as usize / LINE_SIZE;
        debug!("Scanning block {:p} for a hole with last_high_offset {}",
               self, last_high_index);
        let mut low_index = NUM_LINES_PER_BLOCK - 1;
        for index in ((last_high_index + 1)..NUM_LINES_PER_BLOCK) {
            if self.line_counter.get(&index).map_or(true, |c| *c == 0) {
                // +1 to skip the next line in case an object straddles lines
                low_index = index + 1;
                break;
            }
        }
        let mut high_index = NUM_LINES_PER_BLOCK;
        for index in (low_index..NUM_LINES_PER_BLOCK) {
            if self.line_counter.get(&index).map_or(false, |c| *c != 0) {
                high_index = index;
                break;
            }
        }
        if low_index == high_index && high_index != (NUM_LINES_PER_BLOCK - 1) {
            debug!("Rescan: Found single line hole? in block {:p}", self);
            return self.scan_block((high_index * LINE_SIZE - 1) as u16);
        } else if low_index < (NUM_LINES_PER_BLOCK - 1) {
            debug!("Found low index {} and high index {} in block {:p}",
                   low_index, high_index, self);
            return Some(((low_index * LINE_SIZE) as u16,
                         (high_index * LINE_SIZE - 1) as u16));
        }
        debug!("Found no hole in block {:p}", self);
        return None;
    }

    /// Count the holes in this block.
    ///
    /// Holes are lines with no objects allocated.
    pub fn count_holes(&mut self) {
        let holes = self.line_counter.values()
            .fold((0, false), |(holes, in_hole), &elem|
                  match (in_hole, elem) {
                    (false, 0) => (holes + 1, true),
                    (_, _) => (holes, false),
                  }).0;
        self.hole_count = holes;
    }
}

impl BlockInfo{
    /// Returns true if this block is allocated and the address is within the
    /// bounds of this block.
    fn is_in_block(&self, object: GCObjectRef) -> bool {
        // This works because we get zeroed memory from the OS, so
        // self.allocated will be false if this block is not initialized and
        // this method gets only called for objects within the ImmixSpace.
        // After the first initialization the field is properly managed.
        if self.allocated {
            let self_ptr = self as *const BlockInfo as *const u8;
            let self_bound = unsafe{ self_ptr.offset(BLOCK_SIZE as isize)};
            return self_ptr < (object as *const u8)
                && (object as *const u8) <  self_bound;
        }
        return false;
    }

    /// Convert an address on this block into a line number.
    fn object_to_line_num(object: GCObjectRef) -> usize {
        return (object as usize % BLOCK_SIZE) / LINE_SIZE;
    }

    /// Update the line counter for the given object.
    ///
    /// Increment if `increment`, otherwise do a saturating substraction.
    fn update_line_nums(&mut self, object: GCObjectRef, increment: bool) {
        // This calculates how many lines are affected starting from a
        // LINE_SIZE aligned address. So it might not mark enough lines. But
        // that does not matter as we always skip a line in scan_block()
        let line_num = BlockInfo::object_to_line_num(object);
        let object_size = unsafe{ (*object).object_size() };
        for line in line_num..(line_num + (object_size / LINE_SIZE) + 1) {
            match increment {
                true => {
                    if self.line_counter.contains_key(&line) {
                        if let Some(val) = self.line_counter.get_mut(&line) {
                            *val += 1;
                        }
                    } else { self.line_counter.insert(line, 1); }
                    debug!("Incremented line count for line {} to {}", line,
                           self.line_counter.get(&line).unwrap());
                },
                false => {
                    if self.line_counter.contains_key(&line) {
                        if let Some(val) = self.line_counter.get_mut(&line) {
                            *val = Int::saturating_sub(*val, 1);
                        }
                    } else { self.line_counter.insert(line, 0); }
                    debug!("Decremented line count for line {} to {}", line,
                           self.line_counter.get(&line).unwrap());
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use select::SelectCommand;
use super::labeled_drilldown::LabeledDrilldown;
use command::{Command, Query};
use selectable::fragmentable::Fragmentable;
use command_query::CommandQuery;
use queryable::Queryable;
use commandable::Commandable;
use command_line::CommandLine;

pub struct LabeledDrilldownSequenceBuilder {
    select: SelectCommand,
    drilldowns: Vec<LabeledDrilldown>,
}

/// LabeledDrilldownSequence builder.
///
/// ## Simple usage
///
/// ```
/// use ruroonga_command::select::SelectCommand;
/// use ruroonga_command::selectable::labeled_drilldown::LabeledDrilldown;
/// use ruroonga_command::selectable::labeled_drilldown_sequence_builder as seq;
/// let select = SelectCommand::new("Memos".to_string());
/// let drilldown_label1 = LabeledDrilldown::new("label1".to_string())
///                  .table("label2".to_string())
///                  .keys(vec![("category".to_string())])
///                  .output_columns(vec![("_key".to_string()),
///                                       ("_nsubrecs".to_string())]);
/// let drilldown_label2 = LabeledDrilldown::new("label2".to_string())
///                  .keys(vec![("tag".to_string())])
///                  .output_columns(vec![("_key".to_string()),
///                                       ("_nsubrecs".to_string()),
///                                       ("category".to_string())]);
/// let mut builder =
///     seq::LabeledDrilldownSequenceBuilder::new(select,
///                                               vec![(drilldown_label1),
///                                                    (drilldown_label2)]);
/// let _ = builder.build();
/// ```
/// In more practical example, please refer to drilldown_builder examples
/// in [examples/advanced_drilldown.rs]
/// (https://github.com/cosmo0920/ruroonga_command/blob/master/examples/advanced_drilldown.rs).

impl LabeledDrilldownSequenceBuilder {
    pub fn new(select: SelectCommand,
               drilldowns: Vec<LabeledDrilldown>)
               -> LabeledDrilldownSequenceBuilder {
        LabeledDrilldownSequenceBuilder {
            select: select,
            drilldowns: drilldowns,
        }
    }

    pub fn build(self) -> (Command, Query) {
        let mut query: Query = vec![];
        let (command, ordered_select, select) = self.select.to_fragment();
        for ordered in ordered_select.iter() {
            query.push(ordered.to_owned());
        }
        for (key, value) in select.iter() {
            query.push((key.to_owned(), value.to_owned()));
        }
        for drilldown in self.drilldowns.iter() {
            let (_, _, drilldown) = drilldown.clone().to_fragment();
            for (key, value) in drilldown.iter() {
                query.push((key.to_owned(), value.to_owned()));
            }
        }
        (command, query)
    }
}

impl Queryable for LabeledDrilldownSequenceBuilder {
    fn to_query(self) -> String {
        let (command, query) = self.build();
        let mut command = CommandQuery::new(command, query);
        command.encode()
    }
}

impl Commandable for LabeledDrilldownSequenceBuilder {
    fn to_command(self) -> String {
        let (command, query) = self.build();
        let mut command = CommandLine::new(command, query);
        command.encode()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use queryable::Queryable;
    use commandable::Commandable;
    use select::SelectCommand;
    use selectable::labeled_drilldown::LabeledDrilldown;

    #[test]
    fn test_to_query() {
        let select = SelectCommand::new("Entries".to_string())
                         .filter("content @ \"fast\"".to_string());
        let drilldown = LabeledDrilldown::new("label".to_string()).keys(vec![("tag".to_string())]);
        let builder = LabeledDrilldownSequenceBuilder::new(select, vec![(drilldown)]).to_query();
        let encoded = "/d/select?table=Entries&filter=%27content+%40+%22fast%22%27&drilldown%5Blab\
                       el%5D.keys=%27tag%27"
                          .to_string();
        assert_eq!(encoded, builder);
    }

    #[test]
    fn test_to_command() {
        let select = SelectCommand::new("Entries".to_string())
                         .filter("content @ \"fast\"".to_string());
        let drilldown = LabeledDrilldown::new("label".to_string()).keys(vec![("tag".to_string())]);
        let builder = LabeledDrilldownSequenceBuilder::new(select, vec![(drilldown)]).to_command();
        let encoded = "select --table Entries --filter \'content @ \"fast\"\' \
                       --drilldown[label].keys \'tag\'"
                          .to_string();
        assert_eq!(encoded, builder);
    }
}
<commit_msg>rustdoc: Fix document<commit_after>use select::SelectCommand;
use super::labeled_drilldown::LabeledDrilldown;
use command::{Command, Query};
use selectable::fragmentable::Fragmentable;
use command_query::CommandQuery;
use queryable::Queryable;
use commandable::Commandable;
use command_line::CommandLine;

pub struct LabeledDrilldownSequenceBuilder {
    select: SelectCommand,
    drilldowns: Vec<LabeledDrilldown>,
}

/// LabeledDrilldownSequence builder.
///
/// ## Simple usage
///
/// ```
/// use ruroonga_command::select::SelectCommand;
/// use ruroonga_command::selectable::labeled_drilldown::LabeledDrilldown;
/// use ruroonga_command::selectable::labeled_drilldown_sequence_builder as seq;
/// let select = SelectCommand::new("Memos".to_string());
/// let drilldown_label1 = LabeledDrilldown::new("label1".to_string())
///                  .table("label2".to_string())
///                  .keys(vec![("category".to_string())])
///                  .output_columns(vec![("_key".to_string()),
///                                       ("_nsubrecs".to_string())]);
/// let drilldown_label2 = LabeledDrilldown::new("label2".to_string())
///                  .keys(vec![("tag".to_string())])
///                  .output_columns(vec![("_key".to_string()),
///                                       ("_nsubrecs".to_string()),
///                                       ("category".to_string())]);
/// let mut builder =
///     seq::LabeledDrilldownSequenceBuilder::new(select,
///                                               vec![(drilldown_label1),
///                                                    (drilldown_label2)]);
/// let _ = builder.build();
/// ```
/// In more practical example, please refer to advanced_drilldown_to_command example
/// in [examples/advanced_drilldown.rs]
/// (https://github.com/cosmo0920/ruroonga_command/blob/master/examples/advanced_drilldown.rs).

impl LabeledDrilldownSequenceBuilder {
    pub fn new(select: SelectCommand,
               drilldowns: Vec<LabeledDrilldown>)
               -> LabeledDrilldownSequenceBuilder {
        LabeledDrilldownSequenceBuilder {
            select: select,
            drilldowns: drilldowns,
        }
    }

    pub fn build(self) -> (Command, Query) {
        let mut query: Query = vec![];
        let (command, ordered_select, select) = self.select.to_fragment();
        for ordered in ordered_select.iter() {
            query.push(ordered.to_owned());
        }
        for (key, value) in select.iter() {
            query.push((key.to_owned(), value.to_owned()));
        }
        for drilldown in self.drilldowns.iter() {
            let (_, _, drilldown) = drilldown.clone().to_fragment();
            for (key, value) in drilldown.iter() {
                query.push((key.to_owned(), value.to_owned()));
            }
        }
        (command, query)
    }
}

impl Queryable for LabeledDrilldownSequenceBuilder {
    fn to_query(self) -> String {
        let (command, query) = self.build();
        let mut command = CommandQuery::new(command, query);
        command.encode()
    }
}

impl Commandable for LabeledDrilldownSequenceBuilder {
    fn to_command(self) -> String {
        let (command, query) = self.build();
        let mut command = CommandLine::new(command, query);
        command.encode()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use queryable::Queryable;
    use commandable::Commandable;
    use select::SelectCommand;
    use selectable::labeled_drilldown::LabeledDrilldown;

    #[test]
    fn test_to_query() {
        let select = SelectCommand::new("Entries".to_string())
                         .filter("content @ \"fast\"".to_string());
        let drilldown = LabeledDrilldown::new("label".to_string()).keys(vec![("tag".to_string())]);
        let builder = LabeledDrilldownSequenceBuilder::new(select, vec![(drilldown)]).to_query();
        let encoded = "/d/select?table=Entries&filter=%27content+%40+%22fast%22%27&drilldown%5Blab\
                       el%5D.keys=%27tag%27"
                          .to_string();
        assert_eq!(encoded, builder);
    }

    #[test]
    fn test_to_command() {
        let select = SelectCommand::new("Entries".to_string())
                         .filter("content @ \"fast\"".to_string());
        let drilldown = LabeledDrilldown::new("label".to_string()).keys(vec![("tag".to_string())]);
        let builder = LabeledDrilldownSequenceBuilder::new(select, vec![(drilldown)]).to_command();
        let encoded = "select --table Entries --filter \'content @ \"fast\"\' \
                       --drilldown[label].keys \'tag\'"
                          .to_string();
        assert_eq!(encoded, builder);
    }
}
<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;

macro_rules! set_zs {
    ($cpu_state:expr,$val:expr) => ($cpu_state.Z = $val == 0;$cpu_state.S = $val >= 0x80 );
}

macro_rules! stack_push8 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write8(0x0100 | $cpu_state.sp,$val)); $cpu_state.sp-=1;);
}
macro_rules! stack_push16 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write16(0x0100 as u16 | $cpu_state.sp as u16 -1,$val)); $cpu_state.sp-=2;);
}
macro_rules! stack_pull8 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=1;
            $mem.read8(0x0100 | $cpu_state.sp;)
        }
    }
}
macro_rules! stack_pull16 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=2;
            $mem.read16(0x0100 as u16 | $cpu_state.sp as u16 -1)
        }
    }
}

#[derive(Debug)]
pub enum ExecutionError {
  MemoryError(::memory::MemoryError),
  UnexpectedOpcode(String),
  UnexpectedAddressMode(String)
}
impl From<::memory::MemoryError> for ExecutionError {
    fn from(err: ::memory::MemoryError) -> ExecutionError {
        ExecutionError::MemoryError(err)
    }
}

pub struct CpuExecutor {
    op_table: Vec<OpcodeExecInfo>,
}

impl CpuExecutor {
	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        CpuExecutor {
            op_table:  opcodes,
        }
    }

	/// Reset the given cpu_state.
    pub fn power_on(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        self.reset(cpu_state,mem);
    }
	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        try!(self.fetch_and_decode(cpu_state,mem));
        try!(self.execute(cpu_state,mem));
        Ok(())
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = try!(self.decode(cpu_state,mem));
        cpu_state.pc = cpu_state.pc+1;
        Ok(())
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> Result<DecodeRegister,ExecutionError> {
        let mut dr = DecodeRegister {
            info : self.op_table[cpu_state.instruction_register as usize].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.value_final = Some(mem.read8(cpu_state.pc+1).unwrap());

                // NOTE: relative is from the end of the current instruction and relative
                // instructions are 2 bytes long, so we add 2 before adding in the specified offset
                //
                if dr.value_final.unwrap() < 0x80 { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16); }
                else                              { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16 - 0x100);}
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => { return Err(ExecutionError::UnexpectedAddressMode(format!("unrecognized addressing mode '{:?}' while decoding instruction_register!",dr.info.address_mode))); }
        }

        return Ok(dr);
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) -> Result<(),ExecutionError> {
    	// Figure out which opcode is being executed.
    	match cpu_state.decode_register.info.opcode_class {
    		OpcodeClass::ASL => {
				cpu_state.a = cpu_state.a << 1;
				cpu_state.C = (128 & cpu_state.a) > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BCC => {
                if !cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BCS => {
                if cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BEQ => {
                if cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BNE => {
                if !cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::CLC => {
                cpu_state.C = false;
    		},
    		OpcodeClass::LDA => {
    			cpu_state.a = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDX => {
    			cpu_state.x = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.x);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDY => {
    			cpu_state.y = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.y);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LSR => {
    			cpu_state.a = cpu_state.a >> 1;
    			cpu_state.C = 1 & cpu_state.a > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::JMP => {
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::JSR => {
                stack_push16!(cpu_state,mem,cpu_state.pc-1);
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::NOP => {
            },
    		OpcodeClass::SEC => {
                cpu_state.C = true;
    		},
    		OpcodeClass::STA => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.a));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::STX => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.decode_register.value_final.unwrap()));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},

			_ => { return Err(ExecutionError::UnexpectedOpcode(format!("Unrecognised opcode class: {:?}", cpu_state.decode_register.info.opcode_class)));}

    	}
        Ok(())
    }
}

<commit_msg>fixed STX opcode<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use cpu::common_defs::opcode_class::OpcodeClass;
use memory::Memory;

macro_rules! set_zs {
    ($cpu_state:expr,$val:expr) => ($cpu_state.Z = $val == 0;$cpu_state.S = $val >= 0x80 );
}

macro_rules! stack_push8 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write8(0x0100 | $cpu_state.sp,$val)); $cpu_state.sp-=1;);
}
macro_rules! stack_push16 {
    ($cpu_state:expr,$mem:expr,$val:expr) => (try!($mem.write16(0x0100 as u16 | $cpu_state.sp as u16 -1,$val)); $cpu_state.sp-=2;);
}
macro_rules! stack_pull8 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=1;
            $mem.read8(0x0100 | $cpu_state.sp;)
        }
    }
}
macro_rules! stack_pull16 {
    ($cpu_state:expr,$mem:expr) => {
        {
            $cpu_state.pc+=2;
            $mem.read16(0x0100 as u16 | $cpu_state.sp as u16 -1)
        }
    }
}

#[derive(Debug)]
pub enum ExecutionError {
  MemoryError(::memory::MemoryError),
  UnexpectedOpcode(String),
  UnexpectedAddressMode(String)
}
impl From<::memory::MemoryError> for ExecutionError {
    fn from(err: ::memory::MemoryError) -> ExecutionError {
        ExecutionError::MemoryError(err)
    }
}

pub struct CpuExecutor {
    op_table: Vec<OpcodeExecInfo>,
}

impl CpuExecutor {
	/// Construct a new CpuExecutor.
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        CpuExecutor {
            op_table:  opcodes,
        }
    }

	/// Reset the given cpu_state.
    pub fn power_on(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        self.reset(cpu_state,mem);
    }
	/// Reset the given cpu_state.
    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    /// Execute a single instruction in the given memory and cpu_state context.
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        try!(self.fetch_and_decode(cpu_state,mem));
        try!(self.execute(cpu_state,mem));
        Ok(())
    }

	/// Fetch the next instruction and perform address resolution.
    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) -> Result<(),ExecutionError> {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = try!(self.decode(cpu_state,mem));
        cpu_state.pc = cpu_state.pc+1;
        Ok(())
    }

    /// Perform address resolution, returning the info in a DecodeRegister.
    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> Result<DecodeRegister,ExecutionError> {
        let mut dr = DecodeRegister {
            info : self.op_table[cpu_state.instruction_register as usize].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { },
            AddressMode::Implied      => { },

            // explicit addresses from here on out
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.x as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some( ((mem.read16(cpu_state.pc+1).unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Immediate       => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Indirect        => {
                dr.addr_intermediate = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
            },
            AddressMode::IndexedIndirect => {
                dr.addr_intermediate = Some( (mem.read8(cpu_state.pc+1).unwrap() + cpu_state.x) as u16);
                dr.addr_final        = Some(mem.read16(dr.addr_intermediate.unwrap()).unwrap());
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::IndirectIndexed => {
                dr.addr_init         = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.addr_intermediate = Some(mem.read16(dr.addr_init.unwrap()).unwrap());
                dr.addr_final        = Some( ((dr.addr_intermediate.unwrap() as u32 + cpu_state.y as u32) % 65536) as u16);
                dr.value_final       = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Relative        => {
                dr.value_final = Some(mem.read8(cpu_state.pc+1).unwrap());

                // NOTE: relative is from the end of the current instruction and relative
                // instructions are 2 bytes long, so we add 2 before adding in the specified offset
                //
                if dr.value_final.unwrap() < 0x80 { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16); }
                else                              { dr.addr_final  = Some(cpu_state.pc + 2 + dr.value_final.unwrap() as u16 - 0x100);}
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.x as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageY       => {
                dr.addr_final  = Some( (mem.read8(cpu_state.pc+1).unwrap() as u16 + cpu_state.y as u16) % 256);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            _ => { return Err(ExecutionError::UnexpectedAddressMode(format!("unrecognized addressing mode '{:?}' while decoding instruction_register!",dr.info.address_mode))); }
        }

        return Ok(dr);
    }

    /// Perform the current instruction, returning the CpuState after execution.
    pub fn execute(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) -> Result<(),ExecutionError> {
    	// Figure out which opcode is being executed.
    	match cpu_state.decode_register.info.opcode_class {
    		OpcodeClass::ASL => {
				cpu_state.a = cpu_state.a << 1;
				cpu_state.C = (128 & cpu_state.a) > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::BCC => {
                if !cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BCS => {
                if cpu_state.C { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BEQ => {
                if cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else           { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::BNE => {
                if !cpu_state.Z { cpu_state.pc = cpu_state.decode_register.addr_final.unwrap(); }
                else            { cpu_state.pc += cpu_state.decode_register.info.len as u16-1;  }
    		},
    		OpcodeClass::CLC => {
                cpu_state.C = false;
    		},
    		OpcodeClass::LDA => {
    			cpu_state.a = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.a);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDX => {
    			cpu_state.x = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.x);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LDY => {
    			cpu_state.y = cpu_state.decode_register.value_final.unwrap();
                set_zs!(cpu_state,cpu_state.y);

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::LSR => {
    			cpu_state.a = cpu_state.a >> 1;
    			cpu_state.C = 1 & cpu_state.a > 0;
    			if cpu_state.a == 0 { cpu_state.Z = true; }

                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::JMP => {
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::JSR => {
                stack_push16!(cpu_state,mem,cpu_state.pc-1);
    			cpu_state.pc = cpu_state.decode_register.addr_final.unwrap();
    		},
    		OpcodeClass::NOP => {
            },
    		OpcodeClass::SEC => {
                cpu_state.C = true;
    		},
    		OpcodeClass::STA => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.a));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},
    		OpcodeClass::STX => {
                try!(mem.write8(cpu_state.decode_register.addr_final.unwrap(),cpu_state.x));
                cpu_state.pc += cpu_state.decode_register.info.len as u16-1;
    		},

			_ => { return Err(ExecutionError::UnexpectedOpcode(format!("Unrecognised opcode class: {:?}", cpu_state.decode_register.info.opcode_class)));}

    	}
        Ok(())
    }
}

<|endoftext|>


<commit_before>//! This module is UNSTABLE, the reason is - just added.

use std::convert::From;
use std::io::{self, Read, BufRead};
use std::str::{from_utf8, Utf8Error};

use super::{read_marker};
use super::{
    ReadError,
    MarkerReadError,
};
use super::{BigEndianRead};

use super::super::init::Marker;
use super::super::value::ValueRef;

// TODO: Display trait.
#[derive(Debug)]
pub enum Error<'r> {
    /// Failed to fill the internal reader buffer.
    ///
    /// RMP tries to obtain the buffer at the beginning of read operation using `read_buf` function.
    ///
    /// According to the Rust documentation, `fill_buf` function will return an I/O error if the
    /// underlying reader was read, but returned an error.
    InvalidBufferFill(io::Error),
    /// Failed to read the type marker value.
    InvalidMarkerRead(ReadError),
    /// Failed to read string/array/map size.
    InvalidLengthRead(ReadError),
    /// Failed to read packed non-marker data.
    InvalidDataRead(ReadError),

    // length overflow

    /// Failed to interpret a byte slice as a UTF-8 string.
    ///
    /// Contains untouched bytearray with the underlying decoding error.
    InvalidUtf8(&'r [u8], Utf8Error),
}

impl<'r> From<MarkerReadError> for Error<'r> {
    fn from(err: MarkerReadError) -> Error<'r> {
        Error::InvalidMarkerRead(From::from(err))
    }
}

fn read_length<R, D>(rd: &mut R) -> Result<D, ReadError>
    where R: Read,
          D: BigEndianRead
{
    D::read(rd).map_err(From::from)
}

// NOTE: Consumes nothing from the given `BufRead` either on success or fail.
pub fn read_value_ref<R>(rd: &mut R) -> Result<ValueRef, Error>
    where R: BufRead
{
    let mut buf = try!(rd.fill_buf().map_err(|err| Error::InvalidBufferFill(err)));

    // Reading the marker involves either 1 byte read or nothing. On success consumes strictly
    // 1 byte from the `buf`, not from the `rd`.
    let marker = try!(read_marker(&mut buf));

    let val = match marker {
        Marker::FixedString(len) => {
            // Impossible to panic, since u8 always fits in usize.
            let len = len as usize;

            if len > buf.len() {
                return Err(Error::InvalidDataRead(ReadError::UnexpectedEOF));
            }

            // Take a slice.
            let buf = &buf[..len];

            // Try to decode sliced buffer as UTF-8.
            let res = try!(from_utf8(buf).map_err(|err| Error::InvalidUtf8(buf, err)));

            ValueRef::String(res)
        }
        Marker::Str8 => {
            let len: u8 = try!(read_length(&mut buf).map_err(|err| Error::InvalidLengthRead(err)));

            // Impossible to panic, since u8 always fits in usize.
            let len = len as usize;

            if len > buf.len() {
                return Err(Error::InvalidDataRead(ReadError::UnexpectedEOF));
            }

            // Take a slice.
            let buf = &buf[..len];

            // Try to decode sliced buffer as UTF-8.
            let res = try!(from_utf8(buf).map_err(|err| Error::InvalidUtf8(buf, err)));

            ValueRef::String(res)
        }
        _ => unimplemented!(),
    };

    Ok(val)
}

// TODO: Concepts: less code; more tests; fast refactoring after each test; no more than 5 min for
// each red-yellow-green.
<commit_msg>Refactoring: code decomposition.<commit_after>//! This module is UNSTABLE, the reason is - just added.

use std::convert::From;
use std::io::{self, Read, BufRead};
use std::str::{from_utf8, Utf8Error};

use super::{read_marker};
use super::{
    ReadError,
    MarkerReadError,
};
use super::{BigEndianRead};

use super::super::init::Marker;
use super::super::value::ValueRef;

// TODO: Display trait.
#[derive(Debug)]
pub enum Error<'r> {
    /// Failed to fill the internal reader buffer.
    ///
    /// RMP tries to obtain the buffer at the beginning of read operation using `read_buf` function.
    ///
    /// According to the Rust documentation, `fill_buf` function will return an I/O error if the
    /// underlying reader was read, but returned an error.
    InvalidBufferFill(io::Error),
    /// Failed to read the type marker value.
    InvalidMarkerRead(ReadError),
    /// Failed to read string/array/map size.
    InvalidLengthRead(ReadError),
    /// Failed to read packed non-marker data.
    InvalidDataRead(ReadError),

    // length overflow

    /// Failed to interpret a byte slice as a UTF-8 string.
    ///
    /// Contains untouched bytearray with the underlying decoding error.
    InvalidUtf8(&'r [u8], Utf8Error),
}

impl<'r> From<MarkerReadError> for Error<'r> {
    fn from(err: MarkerReadError) -> Error<'r> {
        Error::InvalidMarkerRead(From::from(err))
    }
}

fn read_length<R, D>(rd: &mut R) -> Result<D, ReadError>
    where R: Read,
          D: BigEndianRead
{
    D::read(rd).map_err(From::from)
}

fn read_str(buf: &[u8], len: usize) -> Result<&str, Error> {
    if len > buf.len() {
        return Err(Error::InvalidDataRead(ReadError::UnexpectedEOF));
    }

    // Take a slice.
    let buf = &buf[..len];

    // Try to decode sliced buffer as UTF-8.
    let res = try!(from_utf8(buf).map_err(|err| Error::InvalidUtf8(buf, err)));

    Ok(res)
}

// NOTE: Consumes nothing from the given `BufRead` either on success or fail.
pub fn read_value_ref<R>(rd: &mut R) -> Result<ValueRef, Error>
    where R: BufRead
{
    let mut buf = try!(rd.fill_buf().map_err(|err| Error::InvalidBufferFill(err)));

    // Reading the marker involves either 1 byte read or nothing. On success consumes strictly
    // 1 byte from the `buf`, not from the `rd`.
    let marker = try!(read_marker(&mut buf));

    let val = match marker {
        Marker::FixedString(len) => {
            // Impossible to panic, since u8 always fits in usize.
            let len = len as usize;

            let res = try!(read_str(buf, len));

            ValueRef::String(res)
        }
        Marker::Str8 => {
            let len: u8 = try!(read_length(&mut buf).map_err(|err| Error::InvalidLengthRead(err)));

            // Impossible to panic, since u8 always fits in usize.
            let len = len as usize;

            let res = try!(read_str(buf, len));

            ValueRef::String(res)
        }
        _ => unimplemented!(),
    };

    Ok(val)
}

// TODO: Concepts: less code; more tests; fast refactoring after each test; no more than 5 min for
// each red-yellow-green.
<|endoftext|>


<commit_before>// -*- rust -*-

// Regression test for circular_buffer initialization

use std;

import std.option;
import std._uint;
import std._vec;

// 12-byte unit for the channel buffer. Assuming that the default
// buffer size needs to hold 8 units, then the minimum buffer size
// needs to be 96. That's not a power of two so needs to be rounded up.
type record = rec(i32 val1, i32 val2, i32 val3);

impure fn worker(chan[record] channel) {
    let record val = rec(val1=0i32, val2=0i32, val3=0i32);
    channel <| val;
}

impure fn main() {
    let port[record] myport = port();
    auto mychan = chan(myport);

    auto temp = spawn worker(mychan);
    auto val <- myport;
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Simplify the test for circular_buffer initialization<commit_after>// -*- rust -*-

// Regression tests for circular_buffer when using a unit
// that has a size that is not a power of two

use std;

import std.option;
import std._uint;
import std._vec;

// A 12-byte unit to send over the channel
type record = rec(i32 val1, i32 val2, i32 val3);

// Assuming that the default buffer size needs to hold 8 units,
// then the minimum buffer size needs to be 96. That's not a
// power of two so needs to be rounded up. Don't trigger any
// assertions.
impure fn test_init() {
    let port[record] myport = port();
    auto mychan = chan(myport);

    let record val = rec(val1=0i32, val2=0i32, val3=0i32);

    mychan <| val;
}

impure fn main() {
    test_init();
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>