<commit_before>
use std;
import std::io;
import std::io::{writer_util, reader_util};
import result;

tag state {
    start(bool);
    field(uint, uint);
    escapedfield(uint, uint);
    inquote(uint, uint);
}

type rowreader = {
    delim: char,
    quote: char,
    f : io::reader,
    mutable offset : uint,
    mutable buffers : [@[char]],
    mutable state : state
};

type row = {
    mutable fields : [ field ]
};

enum field {
    bufferfield([@[char]], uint, uint);
    emptyfield();
}

iface rowiter {
    fn readrow() -> result::t<row, str>;
}

iface rowaccess {
    fn len() -> uint;
    fn getchars(uint) -> [char];
    fn getstr(uint) -> str;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    let r : rowreader = {
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : start(false)
    };
    ret r;
}

impl of rowaccess for row {
    fn len() -> uint {
        vec::len(self.fields)
    }
    fn getchars(field: uint) -> [char] {
        alt self.fields[field] {
            emptyfield() { ret []; }
            bufferfield(buffers, so, eo) {
                let r : [char] = [];
                let i = 0u;
                while i < vec::len(buffers) {
                    let from = i == 0u ? so : 0u;
                    let to = (i == vec::len(buffers) - 1u) ? eo : vec::len(*buffers[i]);
                    r += vec::slice(*buffers[i], from, to);
                    i = i + 1u;
                }
                ret r;
            }
        };
    }
    fn getstr(field: uint) -> str {
        ret str::from_chars(self.getchars(field));
    }
}

impl of rowiter for rowreader {
    fn readrow() -> result::t<row, str> {
        fn row_from_buf(self: rowreader, &r: row) -> bool {
            fn new_bufferfield(self: rowreader, sb: uint, so: uint, eo: uint) -> field {
                let bufs : [@[char]] = vec::slice(self.buffers, sb, vec::len(self.buffers));
                ret bufferfield(bufs, so, eo);
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf: @[char] = self.buffers[cbuffer];
            while self.offset < vec::len(*buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                self.offset += 1u;
                alt self.state {
                    start(after_delim) {
                        //io::println(#fmt("start : after_delim %b", after_delim));
                        if c == self.quote {
                            self.state = escapedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                r.fields += [emptyfield];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = start(true);
                            r.fields += [emptyfield];
                        } else {
                            self.state = field(cbuffer, coffset);
                        }
                    }
                    field(b,o) {
                        //io::println(#fmt("field : %u %u", b, o));
                        if c == '\n' {
                            r.fields += [new_bufferfield(self, b, o, coffset)];
                            ret true;
                        } else if c == self.delim {
                            self.state = start(true);
                            r.fields += [new_bufferfield(self, b, o, coffset)];
                        }
                    }
                    escapedfield(b, o) {
                        //io::println(#fmt("escapedfield : %u %u", b, o));
                        if c == self.quote {
                            self.state = inquote(b, o);
                        } else if c == self.delim {
                            self.state = start(true);
                            r.fields += [new_bufferfield(self, b, o, coffset)];
                        }
                    }
                    inquote(b, o) {
                        //io::println(#fmt("inquote : %u %u", b, o));
                        if c == '\n' {
                            r.fields += [new_bufferfield(self, b, o, coffset)];
                            ret true;
                        } else if c == self.quote {
                            // hmm what to do
                            // self.state = escapedfield(x + [self.quote]);
                        } else if c == self.delim {
                            self.state = start(true);
                            r.fields += [new_bufferfield(self, b, o, coffset)];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
            }
            ret false;
        }

        self.state = start(false);
        let do_read = vec::len(self.buffers) == 0u;
        while true {
            if do_read {
                let data: @[char] = @self.f.read_chars(1024u);
                //io::println(#fmt("len %u '%s'", vec::len(*data), str::from_chars(*data)));
                if vec::len(*data) == 0u {
                    ret result::err("EOF");
                }
                self.buffers += [data];
                self.offset = 0u;
            }

            let r: row = { mutable fields: [] };
            if row_from_buf(self, r) {
                ret result::ok(r);
            }
            do_read = true;
        }
        ret result::err("unreachable");
    }
}

fn main(args : [str])
{
    if (vec::len(args) != 2u) {
        ret;
    }
    let f : io::reader = result::get(io::file_reader(args[1]));
    let reader = new_reader(f, ',', '"');
    while true {
        let res = reader.readrow();
        if result::failure(res) {
            break;
        }
        let row = result::get(res);
        io::println(#fmt("---- ROW %u fields -----", row.len()));
        let i = 0u;
        while i < row.len() {
            io::println(row.getstr(i));
            i = i + 1u;
        }
    }
}

<commit_msg>fix row retention bug<commit_after>
use std;
import std::io;
import std::io::{writer_util, reader_util};
import result;

tag state {
    start(bool);
    field(uint, uint);
    escapedfield(uint, uint);
    inquote(uint, uint);
}

type rowreader = {
    delim: char,
    quote: char,
    f : io::reader,
    mutable offset : uint,
    mutable buffers : [@[char]],
    mutable state : state
};

type row = {
    fields : [ field ]
};

enum field {
    bufferfield([@[char]], uint, uint);
    emptyfield();
}

iface rowiter {
    fn readrow() -> result::t<row, str>;
}

iface rowaccess {
    fn len() -> uint;
    fn getchars(uint) -> [char];
    fn getstr(uint) -> str;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    let r : rowreader = {
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : start(false)
    };
    ret r;
}

impl of rowaccess for row {
    fn len() -> uint {
        vec::len(self.fields)
    }
    fn getchars(field: uint) -> [char] {
        alt self.fields[field] {
            emptyfield() { ret []; }
            bufferfield(buffers, so, eo) {
                let r : [char] = [];
                let i = 0u;
                while i < vec::len(buffers) {
                    let from = i == 0u ? so : 0u;
                    let to = (i == vec::len(buffers) - 1u) ? eo : vec::len(*buffers[i]);
                    r += vec::slice(*buffers[i], from, to);
                    i = i + 1u;
                }
                ret r;
            }
        };
    }
    fn getstr(field: uint) -> str {
        ret str::from_chars(self.getchars(field));
    }
}

impl of rowiter for rowreader {
    fn readrow() -> result::t<row, str> {
        fn row_from_buf(self: rowreader, &fields: [field]) -> bool {
            fn new_bufferfield(self: rowreader, sb: uint, so: uint, eo: uint) -> field {
                let bufs : [@[char]] = vec::slice(self.buffers, sb, vec::len(self.buffers));
                ret bufferfield(bufs, so, eo);
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf: @[char] = self.buffers[cbuffer];
            while self.offset < vec::len(*buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                self.offset += 1u;
                alt self.state {
                    start(after_delim) {
                        //io::println(#fmt("start : after_delim %b", after_delim));
                        if c == self.quote {
                            self.state = escapedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                fields += [emptyfield];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = start(true);
                            fields += [emptyfield];
                        } else {
                            self.state = field(cbuffer, coffset);
                        }
                    }
                    field(b,o) {
                        //io::println(#fmt("field : %u %u", b, o));
                        if c == '\n' {
                            fields += [new_bufferfield(self, b, o, coffset)];
                            ret true;
                        } else if c == self.delim {
                            self.state = start(true);
                            fields += [new_bufferfield(self, b, o, coffset)];
                        }
                    }
                    escapedfield(b, o) {
                        //io::println(#fmt("escapedfield : %u %u", b, o));
                        if c == self.quote {
                            self.state = inquote(b, o);
                        } else if c == self.delim {
                            self.state = start(true);
                            fields += [new_bufferfield(self, b, o, coffset)];
                        }
                    }
                    inquote(b, o) {
                        //io::println(#fmt("inquote : %u %u", b, o));
                        if c == '\n' {
                            fields += [new_bufferfield(self, b, o, coffset)];
                            ret true;
                        } else if c == self.quote {
                            // hmm what to do
                            // self.state = escapedfield(x + [self.quote]);
                        } else if c == self.delim {
                            self.state = start(true);
                            fields += [new_bufferfield(self, b, o, coffset)];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
            }
            ret false;
        }

        self.state = start(false);
        let do_read = vec::len(self.buffers) == 0u;
        let fields = [];
        while true {
            if do_read {
                let data: @[char] = @self.f.read_chars(1024u);
                //io::println(#fmt("len %u '%s'", vec::len(*data), str::from_chars(*data)));
                if vec::len(*data) == 0u {
                    ret result::err("EOF");
                }
                self.buffers += [data];
                self.offset = 0u;
            }

            if row_from_buf(self, fields) {
                let r: row = { fields: fields };
                fields = [];
                if vec::len(self.buffers) > 1u {
                    self.buffers = vec::slice(self.buffers, vec::len(self.buffers) - 1u, vec::len(self.buffers));
                }
                ret result::ok(r);
            }
            do_read = true;
        }
        ret result::err("unreachable");
    }
}

fn main(args : [str])
{
    if (vec::len(args) != 2u) {
        ret;
    }
    let f : io::reader = result::get(io::file_reader(args[1]));
    let reader = new_reader(f, ',', '"');
    while true {
        let res = reader.readrow();
        if result::failure(res) {
            break;
        }
        let row = result::get(res);
        io::println(#fmt("---- ROW %u fields -----", row.len()));
        let i = 0u;
        while i < row.len() {
            io::println(row.getstr(i));
            i = i + 1u;
        }
    }
}

<|endoftext|>


<commit_before>use std;
import io::{writer_util, reader_util};
import std::map;
import map::hashmap;
import result;

export rowreader, rowiter,
       new_reader, new_reader_readlen,
       hashmap_iter, hashmap_iter_full;

enum state {
    fieldstart(bool),
    infield(uint, uint),
    inquotedfield(uint, uint),
    inquote(uint, uint)
}

type rowreader = {
    readlen: uint,
    delim: char,
    quote: char,
    f : io::reader,
    mut offset : uint,
    mut buffers : [[char]],
    mut state : state,
    mut trailing_nl : bool,
    mut terminating : bool
};

type bufferdescr = {
    escaped: bool,
    sb: uint,
    eb: uint,
    start: uint,
    end: uint
};

enum fieldtype {
    emptyfield(),
    bufferfield(bufferdescr)
}

iface rowiter {
    fn readrow(&row: [str]) -> bool;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    {
        new_reader_readlen(f, delim, quote, 1024u)
    }
}

fn new_reader_readlen(+f: io::reader, +delim: char, +quote: char, rl: uint) -> rowreader {
    {
        readlen: rl,
        delim: delim,
        quote: quote,
        f: f,
        mut offset : 0u,
        mut buffers : [],
        mut state : fieldstart(false),
        mut trailing_nl : false,
        mut terminating: false
    }
}

fn statestr(state: state) -> str {
    alt state {
        fieldstart(after_delim) {
            #fmt("fieldstart : after_delim %b", after_delim)
        }
        infield(b,o) {
            #fmt("field : %u %u", b, o)
        }
        inquotedfield(b, o) {
            #fmt("inquotedfield : %u %u", b, o)
        }
        inquote(b, o) {
            #fmt("inquote : %u %u", b, o)
        }
    }
}

fn unescape(escaped: [char]) -> [char] {
    let mut r : [char] = [];
    vec::reserve(r, vec::len(escaped));
    let mut in_q = false;
    for c in escaped {
        if in_q {
            assert(c == '"');
            in_q = false;
        } else {
            in_q = c == '"';
            r += [c];
        }
    }
    ret r;
}

impl of rowiter for rowreader {
    fn readrow(&row: [str]) -> bool {
        fn row_from_buf(self: rowreader, &fields: [str]) -> bool {
            fn decode(buffers: [[char]], field: fieldtype) -> str {
                alt field {
                    emptyfield() { "" }
                    bufferfield(desc) {
                        let mut buf = [];
                        {
                            let mut i = desc.sb;
                            while i <= desc.eb {
                                let from = if (i == desc.sb)
                                    { desc.start } else { 0u };
                                let to = if (i == desc.eb)
                                    { desc.end } else { vec::len(buffers[i]) };
                                buf += vec::slice(buffers[i], from, to);
                                i = i + 1u;
                            }
                        }
                        if desc.escaped {
                            buf = unescape(buf);
                        }
                        str::from_chars(buf)
                    }
                }
            }
            fn new_bufferfield(self: rowreader, escaped: bool, sb: uint, so: uint, eo: uint) -> fieldtype {
                let mut eb = vec::len(self.buffers) - 1u;
                let mut sb = sb, so = so, eo = eo;
                if escaped {
                    so += 1u;
                    if so > vec::len(self.buffers[sb]) {
                        sb += 1u;
                        so = vec::len(self.buffers[sb]) - 1u;
                    }
                    if eo > 0u {
                        eo -= 1u;
                    } else {
                        eb -= 1u;
                        eo = vec::len(self.buffers[eb]) - 1u;
                    }
                }
                bufferfield({ escaped: escaped, sb: sb, eb: eb, start: so, end: eo })
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf = self.buffers[cbuffer];
            while self.offset < vec::len(buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                #debug("got '%c' | %s", c, statestr(self.state));
                self.offset += 1u;
                alt self.state {
                    fieldstart(after_delim) {
                        #debug("fieldstart : after_delim %b", after_delim);
                        if c == self.quote {
                            self.state = inquotedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                fields += [decode(self.buffers, emptyfield)];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [decode(self.buffers, emptyfield)];
                        } else {
                            self.state = infield(cbuffer, coffset);
                        }
                    }
                    infield(b,o) {
                        #debug("field : %u %u", b, o);
                        if c == '\n' {
                            fields += [decode(self.buffers, new_bufferfield(self, false, b, o, coffset))];
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [decode(self.buffers, new_bufferfield(self, false, b, o, coffset))];
                        }
                    }
                    inquotedfield(b, o) {
                        #debug("inquotedfield : %u %u", b, o);
                        if c == self.quote {
                            self.state = inquote(b, o);
                        }
                    }
                    inquote(b, o) {
                        #debug("inquote : %u %u", b, o);
                        if c == '\n' {
                            fields += [decode(self.buffers, new_bufferfield(self, true, b, o, coffset))];
                            ret true;
                        } else if c == self.quote {
                            self.state = inquotedfield(b, o);
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [decode(self.buffers, new_bufferfield(self, true, b, o, coffset))];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
                #debug("now %s", statestr(self.state));
            }
            ret false;
        }
        self.state = fieldstart(false);
        let mut do_read = vec::len(self.buffers) == 0u;

        while !self.terminating {
            if do_read {
                let mut data = self.f.read_chars(self.readlen);
                //log(error, ("aa", str::from_chars(data)));
                if vec::len(data) == 0u {
                    if !self.trailing_nl {
                        self.terminating = true;
                        data = ['\n'];
                    } else {
                        ret false;
                    }
                }
                // this is horrible, but it avoids the whole parser needing
                // to know about \r.
                data = vec::filter(data) { |c| c != '\r' };
                let data_len = vec::len(data);
                if data_len == 0u {
                    cont;
                }
                self.trailing_nl = data[data_len - 1u] == '\n';
                self.buffers += [data];
                self.offset = 0u;
            }

            if row_from_buf(self, row) {
                if vec::len(self.buffers) > 1u {
                    self.buffers = vec::slice(self.buffers, vec::len(self.buffers) - 1u, vec::len(self.buffers));
                }
                ret true;
            }
            do_read = true;
        }
        ret false;
    }
}

fn hashmap_iter_cols(r: rowreader, cols: [str], f: fn(map::hashmap<str, str>)) {
    let mut fields : [str] = [];
    // can reuse, we're just shoving new vals in same cols..
    let m : map::hashmap<str, str> = map::str_hash();
    let ncols = vec::len(cols);
    while r.readrow(fields) {
        if vec::len(fields) != ncols {
            cont; // FIXME: how to flag that we dropped a crazy row?
        }
        let mut col = 0u;
        vec::iter(fields) { |s|
            m.insert(cols[col], s);
            col += 1u;
        };
        f(m);
    }
}

// reads the first row as a header, to derive keys for a hashmap
// emitted for each subsequent row
fn hashmap_iter(r: rowreader, f: fn(map::hashmap<str, str>)) {
    let mut row: [str] = [];
    if r.readrow(row) {
        hashmap_iter_cols(r, row, f);
    }
}

// as hashmap_iter, but first apply 'hc' to each header; allows
// cleaning up headers; also allows verification that heads are
// satisfactory
fn hashmap_iter_full(r: rowreader, hmap: fn(&&h: str) -> str, hver: fn(cols: [str]) -> bool, f: fn(map::hashmap<str, str>)) {
    let mut row: [str] = [];
    if r.readrow(row) {
        let cols : [str] = vec::map(row, hmap);
        if !hver(cols) {
            ret;
        }
        hashmap_iter_cols(r, cols, f);
    }
}

#[cfg(test)]
mod test {
    fn rowmatch(testdata: str, expected: [[str]]) {
        let chk = fn@(s: str, mk: fn(io::reader) -> rowreader) {
            let f = io::str_reader(s);
            let r = mk(f);
            let mut i = 0u;
            loop {
                let mut row: [str] = [];
                if !r.readrow(row) {
                    break;
                }
                let expect = expected[i];
                assert(vec::len(row) == vec::len(expect));
                let mut j = 0u;
                while j < row.len() {
                    assert(row[j] == expect[j]);
                    j += 1u;
                }
                i += 1u;
            }
            assert(i == vec::len(expected));
        };
        let runchecks = fn@(s: str) {
            // test default reader params
            chk(s) { |inp|
                new_reader_readlen(inp, ',', '"', 2u)
            };
            // test default constructor
            chk(s) { |inp|
                new_reader(inp, ',', '"')
            };
            // test continuations over read buffers
            let mut j = 1u;
            while j < str::len(s) {
                chk(s) { |inp|
                    new_reader_readlen(inp, ',', '"', j)
                };
                j += 1u;
            }
            ret;
        };
        // so we can test trailing newline case, testdata
        // must not end in \n - leave off the last newline
        runchecks(testdata);
        runchecks(str::replace(testdata, "\n", "\r\n"));
        if !str::ends_with(testdata, "\n") {
            runchecks(testdata+"\n");
            runchecks(str::replace(testdata+"\n", "\n", "\r\n"));
        }
    }

    #[test]
    fn simple() {
        rowmatch("a,b,c,d\n1,2,3,4",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4"]]);
    }

    #[test]
    fn trailing_comma() {
        rowmatch("a,b,c,d\n1,2,3,4,",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4", ""]]);
    }

    #[test]
    fn leading_comma() {
        rowmatch("a,b,c,d\n,1,2,3,4",
                 [["a", "b", "c", "d"], ["", "1", "2", "3", "4"]]);
    }

    #[test]
    fn quote_simple() {
        rowmatch("\"Hello\",\"There\"\na,b,\"c\",d",
                 [["Hello", "There"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn quote_nested() {
        rowmatch("\"Hello\",\"There is a \"\"fly\"\" in my soup\"\na,b,\"c\",d",
                 [["Hello", "There is a \"fly\" in my soup"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn quote_with_comma() {
        rowmatch("\"1,2\"",
                 [["1,2"]])
    }

    #[test]
    fn quote_with_other_comma() {
        rowmatch("1,2,3,\"a,b,c\"",
                 [["1", "2", "3", "a,b,c"]])
    }

    #[test]
    fn blank_line() {
        rowmatch("\n\n", [[], []]);
    }
}

<commit_msg>a bunch of speedups<commit_after>use std;
import io::{writer_util, reader_util};
import std::map;
import map::hashmap;
import result;

export rowreader, rowiter,
       new_reader, new_reader_readlen,
       hashmap_iter, hashmap_iter_full;

enum state {
    fieldstart(bool),
    infield(uint, uint),
    inquotedfield(uint, uint),
    inquote(uint, uint)
}

type rowreader = {
    readlen: uint,
    delim: char,
    quote: char,
    f : io::reader,
    mut offset : uint,
    mut buffers : [[char]],
    mut bufferlens: [uint],
    mut state : state,
    mut trailing_nl : bool,
    mut terminating : bool
};

type bufferdescr = {
    escaped: bool,
    sb: uint,
    eb: uint,
    start: uint,
    end: uint
};

enum fieldtype {
    emptyfield(),
    bufferfield(bufferdescr)
}

iface rowiter {
    fn readrow(&row: [str]) -> bool;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    {
        new_reader_readlen(f, delim, quote, 1024u)
    }
}

fn new_reader_readlen(+f: io::reader, +delim: char, +quote: char, rl: uint) -> rowreader {
    {
        readlen: rl,
        delim: delim,
        quote: quote,
        f: f,
        mut offset : 0u,
        mut buffers : [],
        mut bufferlens: [],
        mut state : fieldstart(false),
        mut trailing_nl : false,
        mut terminating: false
    }
}

fn statestr(state: state) -> str {
    alt state {
        fieldstart(after_delim) {
            #fmt("fieldstart : after_delim %b", after_delim)
        }
        infield(b,o) {
            #fmt("field : %u %u", b, o)
        }
        inquotedfield(b, o) {
            #fmt("inquotedfield : %u %u", b, o)
        }
        inquote(b, o) {
            #fmt("inquote : %u %u", b, o)
        }
    }
}

fn unescape(escaped: [char]) -> [char] {
    let mut r : [char] = [];
    vec::reserve(r, vec::len(escaped));
    let mut in_q = false;
    for c in escaped {
        if in_q {
            assert(c == '"');
            in_q = false;
        } else {
            in_q = c == '"';
            r += [c];
        }
    }
    ret r;
}

impl of rowiter for rowreader {
    #[inline]
    fn readrow(&row: [str]) -> bool {
        fn row_from_buf(self: rowreader, &fields: [str]) -> bool {
            fn decode(buffers: [[char]], field: fieldtype) -> str {
                alt field {
                    emptyfield() { "" }
                    bufferfield(desc) {
                        let mut buf = [];
                        vec::reserve(buf, 256u);
                        let mut i = desc.sb;
                        while i <= desc.eb {
                            let from = if (i == desc.sb)
                                { desc.start } else { 0u };
                            let to = if (i == desc.eb)
                                { desc.end } else { vec::len(buffers[i]) };
                            let mut j = from;
                            while j < to {
                                buf += [buffers[i][j]];
                                j += 1u;
                            }
                            i = i + 1u;
                        }
                        if desc.escaped {
                            buf = unescape(buf);
                        }
                        str::from_chars(buf)
                    }
                }
            }
            #[inline]
            fn new_bufferfield(self: rowreader, escaped: bool, sb: uint, so: uint, eo: uint) -> fieldtype {
                let mut eb = vec::len(self.buffers) - 1u;
                let mut sb = sb, so = so, eo = eo;
                if escaped {
                    so += 1u;
                    if so > self.bufferlens[sb] {
                        sb += 1u;
                        so = self.bufferlens[sb] - 1u;
                    }
                    if eo > 0u {
                        eo -= 1u;
                    } else {
                        eb -= 1u;
                        eo = self.bufferlens[eb] - 1u;
                    }
                }
                bufferfield({ escaped: escaped, sb: sb, eb: eb, start: so, end: eo })
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf = self.buffers[cbuffer];
            while self.offset < vec::len(buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                #debug("got '%c' | %s", c, statestr(self.state));
                self.offset += 1u;
                alt self.state {
                    fieldstart(after_delim) {
                        #debug("fieldstart : after_delim %b", after_delim);
                        if c == self.quote {
                            self.state = inquotedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                fields += [decode(self.buffers, emptyfield)];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [decode(self.buffers, emptyfield)];
                        } else {
                            self.state = infield(cbuffer, coffset);
                        }
                    }
                    infield(b,o) {
                        #debug("field : %u %u", b, o);
                        if c == '\n' {
                            fields += [decode(self.buffers, new_bufferfield(self, false, b, o, coffset))];
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [decode(self.buffers, new_bufferfield(self, false, b, o, coffset))];
                        }
                    }
                    inquotedfield(b, o) {
                        #debug("inquotedfield : %u %u", b, o);
                        if c == self.quote {
                            self.state = inquote(b, o);
                        }
                    }
                    inquote(b, o) {
                        #debug("inquote : %u %u", b, o);
                        if c == '\n' {
                            fields += [decode(self.buffers, new_bufferfield(self, true, b, o, coffset))];
                            ret true;
                        } else if c == self.quote {
                            self.state = inquotedfield(b, o);
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [decode(self.buffers, new_bufferfield(self, true, b, o, coffset))];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
                #debug("now %s", statestr(self.state));
            }
            ret false;
        }
        self.state = fieldstart(false);
        let mut do_read = vec::len(self.buffers) == 0u;
        row = [];
        while !self.terminating {
            if do_read {
                let mut data = self.f.read_chars(self.readlen);
                if vec::len(data) == 0u {
                    if !self.trailing_nl {
                        self.terminating = true;
                        data = ['\n'];
                    } else {
                        ret false;
                    }
                }
                // this is horrible, but it avoids the whole parser needing
                // to know about \r.
                data = vec::filter(data) { |c| c != '\r' };
                let data_len = vec::len(data);
                if data_len == 0u {
                    cont;
                }
                self.trailing_nl = data[data_len - 1u] == '\n';
                self.buffers += [data];
                self.bufferlens += [data_len];
                self.offset = 0u;
            }

            if row_from_buf(self, row) {
                let buflen = vec::len(self.buffers);
                if buflen > 1u {
                    self.buffers = [self.buffers[buflen-1u]];
                    self.bufferlens = [self.bufferlens[buflen-1u]];
                }
                ret true;
            }
            do_read = true;
        }
        ret false;
    }
}

fn hashmap_iter_cols(r: rowreader, cols: [str], f: fn(map::hashmap<str, str>)) {
    let mut fields : [str] = [];
    // can reuse, we're just shoving new vals in same cols..
    let m : map::hashmap<str, str> = map::str_hash();
    let ncols = vec::len(cols);
    while r.readrow(fields) {
        log(error, fields);
        if vec::len(fields) != ncols {
            cont; // FIXME: how to flag that we dropped a crazy row?
        }
        let mut col = 0u;
        vec::iter(fields) { |s|
            m.insert(cols[col], s);
            col += 1u;
        };
        f(m);
    }
}

// reads the first row as a header, to derive keys for a hashmap
// emitted for each subsequent row
fn hashmap_iter(r: rowreader, f: fn(map::hashmap<str, str>)) {
    let mut row: [str] = [];
    if r.readrow(row) {
        hashmap_iter_cols(r, row, f);
    }
}

// as hashmap_iter, but first apply 'hc' to each header; allows
// cleaning up headers; also allows verification that heads are
// satisfactory
fn hashmap_iter_full(r: rowreader, hmap: fn(&&h: str) -> str, hver: fn(cols: [str]) -> bool, f: fn(map::hashmap<str, str>)) {
    let mut row: [str] = [];
    if r.readrow(row) {
        let cols : [str] = vec::map(row, hmap);
        if !hver(cols) {
            ret;
        }
        hashmap_iter_cols(r, cols, f);
    }
}

#[cfg(test)]
mod test {
    fn rowmatch(testdata: str, expected: [[str]]) {
        let chk = fn@(s: str, mk: fn(io::reader) -> rowreader) {
            let f = io::str_reader(s);
            let r = mk(f);
            let mut i = 0u;
            let mut row: [str] = [];
            loop {
                if !r.readrow(row) {
                    break;
                }
                let expect = expected[i];
                assert(vec::len(row) == vec::len(expect));
                let mut j = 0u;
                while j < row.len() {
                    assert(row[j] == expect[j]);
                    j += 1u;
                }
                i += 1u;
            }
            assert(i == vec::len(expected));
        };
        let runchecks = fn@(s: str) {
            // test default reader params
            chk(s) { |inp|
                new_reader_readlen(inp, ',', '"', 2u)
            };
            // test default constructor
            chk(s) { |inp|
                new_reader(inp, ',', '"')
            };
            // test continuations over read buffers
            let mut j = 1u;
            while j < str::len(s) {
                chk(s) { |inp|
                    new_reader_readlen(inp, ',', '"', j)
                };
                j += 1u;
            }
            ret;
        };
        // so we can test trailing newline case, testdata
        // must not end in \n - leave off the last newline
        runchecks(testdata);
        runchecks(str::replace(testdata, "\n", "\r\n"));
        if !str::ends_with(testdata, "\n") {
            runchecks(testdata+"\n");
            runchecks(str::replace(testdata+"\n", "\n", "\r\n"));
        }
    }

    #[test]
    fn simple() {
        rowmatch("a,b,c,d\n1,2,3,4",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4"]]);
    }

    #[test]
    fn trailing_comma() {
        rowmatch("a,b,c,d\n1,2,3,4,",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4", ""]]);
    }

    #[test]
    fn leading_comma() {
        rowmatch("a,b,c,d\n,1,2,3,4",
                 [["a", "b", "c", "d"], ["", "1", "2", "3", "4"]]);
    }

    #[test]
    fn quote_simple() {
        rowmatch("\"Hello\",\"There\"\na,b,\"c\",d",
                 [["Hello", "There"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn quote_nested() {
        rowmatch("\"Hello\",\"There is a \"\"fly\"\" in my soup\"\na,b,\"c\",d",
                 [["Hello", "There is a \"fly\" in my soup"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn quote_with_comma() {
        rowmatch("\"1,2\"",
                 [["1,2"]])
    }

    #[test]
    fn quote_with_other_comma() {
        rowmatch("1,2,3,\"a,b,c\"",
                 [["1", "2", "3", "a,b,c"]])
    }

    #[test]
    fn blank_line() {
        rowmatch("\n\n", [[], []]);
    }
}

<|endoftext|>


<commit_before>use serde::{Serialize, Serializer};

use std::fmt;
use std::sync::RwLock;
use std::collections::HashSet;
use std::str;
use std::ptr;
use std::cmp::Ordering;
use std::ops::Deref;
use std::hash::{Hash, Hasher};

pub fn leak(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

lazy_static! {
    static ref STRING_CACHE: RwLock<HashSet<&'static str>> =
        RwLock::new(HashSet::new());
}

#[derive(Clone, Copy)]
pub struct InternedString {
    inner: &'static str,
}

impl PartialEq for InternedString {
    fn eq(&self, other: &InternedString) -> bool {
        ptr::eq(self.as_str(), other.as_str())
    }
}

impl Eq for InternedString {}

impl InternedString {
    pub fn new(str: &str) -> InternedString {
        let mut cache = STRING_CACHE.write().unwrap();
        let s = cache.get(str).map(|&s| s).unwrap_or_else(|| {
            let s = leak(str.to_string());
            cache.insert(s);
            s
        });

        InternedString { inner: s }
    }

    pub fn as_str(&self) -> &'static str {
        self.inner
    }
}

impl Deref for InternedString {
    type Target = str;

    fn deref(&self) -> &'static str {
        self.as_str()
    }
}

impl Hash for InternedString {
    // NB: we can't implement this as `identity(self).hash(state)`,
    // because we use this for on-disk fingerprints and so need
    // stability across Cargo invocations.
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_str().hash(state);
    }
}

impl fmt::Debug for InternedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self.as_str(), f)
    }
}

impl fmt::Display for InternedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self.as_str(), f)
    }
}

impl Ord for InternedString {
    fn cmp(&self, other: &InternedString) -> Ordering {
        self.as_str().cmp(other.as_str())
    }
}

impl PartialOrd for InternedString {
    fn partial_cmp(&self, other: &InternedString) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Serialize for InternedString {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.inner)
    }
}
<commit_msg>`Mutex` instead of just using `write` on a `RwLock`<commit_after>use serde::{Serialize, Serializer};

use std::fmt;
use std::sync::Mutex;
use std::collections::HashSet;
use std::str;
use std::ptr;
use std::cmp::Ordering;
use std::ops::Deref;
use std::hash::{Hash, Hasher};

pub fn leak(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

lazy_static! {
    static ref STRING_CACHE: Mutex<HashSet<&'static str>> =
        Mutex::new(HashSet::new());
}

#[derive(Clone, Copy)]
pub struct InternedString {
    inner: &'static str,
}

impl PartialEq for InternedString {
    fn eq(&self, other: &InternedString) -> bool {
        ptr::eq(self.as_str(), other.as_str())
    }
}

impl Eq for InternedString {}

impl InternedString {
    pub fn new(str: &str) -> InternedString {
        let mut cache = STRING_CACHE.lock().unwrap();
        let s = cache.get(str).map(|&s| s).unwrap_or_else(|| {
            let s = leak(str.to_string());
            cache.insert(s);
            s
        });

        InternedString { inner: s }
    }

    pub fn as_str(&self) -> &'static str {
        self.inner
    }
}

impl Deref for InternedString {
    type Target = str;

    fn deref(&self) -> &'static str {
        self.as_str()
    }
}

impl Hash for InternedString {
    // NB: we can't implement this as `identity(self).hash(state)`,
    // because we use this for on-disk fingerprints and so need
    // stability across Cargo invocations.
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_str().hash(state);
    }
}

impl fmt::Debug for InternedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self.as_str(), f)
    }
}

impl fmt::Display for InternedString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self.as_str(), f)
    }
}

impl Ord for InternedString {
    fn cmp(&self, other: &InternedString) -> Ordering {
        self.as_str().cmp(other.as_str())
    }
}

impl PartialOrd for InternedString {
    fn partial_cmp(&self, other: &InternedString) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Serialize for InternedString {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.inner)
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("04.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
struct DateTime {
    year: usize,
    month: usize,
    day: usize,
    hour: usize,
    minute: usize
}

impl fmt::Debug for DateTime {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}-{}-{} {}:{}", self.year, self.month, self.day, self.hour, self.minute)
    }
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
enum Event {
    Begin(usize),
    Sleep,
    WakeUp
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
struct LogEntry {
    time: DateTime,
    event: Event
}

type AsleepPlan = HashMap<usize, HashMap<usize, Vec<DateTime>>>;

fn create_log(input: &str) -> Vec<LogEntry> {
    let mut result: Vec<LogEntry> = input.lines()
        .filter_map(|line| {
            let mut tokens = line.split(' ');

            let mut date = match tokens.next() {
                Some(x) => x[1..].split('-').filter_map(|x| x.parse::<usize>().ok()),
                _ => return None
            };

            let mut time = match tokens.next() {
                Some(x) => x[..x.len() - 1].split(':').filter_map(|x| x.parse::<usize>().ok()),
                _ => return None
            };

            let event = match (tokens.next(), tokens.next()) {
                (Some("wakes"), _) => Event::WakeUp,
                (Some("falls"), _) => Event::Sleep,
                (Some("Guard"), Some(token)) => match token[1..].parse::<usize>() {
                    Ok(id) => Event::Begin(id),
                    _ => return None
                },
                _ => return None
            };

            match (date.next(), date.next(), date.next(), time.next(), time.next()) {
                (Some(year), Some(month), Some(day), Some(hour), Some(minute)) => {
                    Some(LogEntry {
                        time: DateTime {year, month, day, hour, minute},
                        event
                    })
                },
                _ => None
            }
        })
        .collect();

    result.sort();
    result
}

fn create_plan(log: &Vec<LogEntry>) -> AsleepPlan {
    let mut plan = AsleepPlan::new();

    if log.len() == 0 {
        return plan;
    }

    let mut id = 0;
    let mut last_time = log[0].time;
    let mut asleep = true;

    for &LogEntry {time, event} in log.iter() {
        match event {
            Event::Begin(x) => {
                id = x;

                if !plan.contains_key(&id) {
                    plan.insert(id, HashMap::new());
                }
            },
            Event::WakeUp if asleep => {
                if let Some(asleep_map) = plan.get_mut(&id) {
                    for i in last_time.minute..time.minute {
                        if !asleep_map.contains_key(&i) {
                            asleep_map.insert(i, vec![]);
                        }

                        let asleep_vec = asleep_map.get_mut(&i).unwrap();

                        asleep_vec.push(DateTime {
                            year: time.year,
                            month: time.month,
                            day: time.day,
                            hour: time.hour,
                            minute: i
                        });
                    }
                }

                asleep = false;
            },
            Event::Sleep => {
                last_time = time;
                asleep = true;
            },
            _ => {}
        };
    }

    plan
}

fn main() {
    let data = create_log(&get_input().unwrap());
    let plan = create_plan(&data);

    let empty_map = HashMap::new();
    let empty_vec = Vec::new();

    let (&candidate_id, asleep_map) = plan.iter()
        .max_by_key(|&(_, asleep_map)| asleep_map.values().flatten().count())
        .unwrap_or((&0, &empty_map));

    let (&candidate_minute, _) = asleep_map.iter()
        .max_by_key(|&(_, asleep_vec)| asleep_vec.len())
        .unwrap_or((&0, &empty_vec));

    println!("Part 1: {}", candidate_id * candidate_minute);

    let (candidate_id, candidate_minute, _) = plan.iter()
        .map(|(&id, asleep_map)| (
            id,
            asleep_map.iter()
                .map(|(&id, asleep_vec)| (id, asleep_vec.len()))
                .max_by_key(|&(_, value)| value)
                .unwrap_or((0, 0))
        ))
        .map(|(x, (y, z))| (x, y, z))
        .max_by_key(|&(_, _, value)| value)
        .unwrap_or((0, 0, 0));

    println!("Part 2: {}", candidate_id * candidate_minute);
}
<commit_msg>Simplify code<commit_after>use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("04.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
struct DateTime {
    year: usize,
    month: usize,
    day: usize,
    hour: usize,
    minute: usize
}

impl fmt::Debug for DateTime {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}-{}-{} {}:{}", self.year, self.month, self.day, self.hour, self.minute)
    }
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
enum Event {
    Begin(usize),
    Sleep,
    WakeUp
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
struct LogEntry {
    time: DateTime,
    event: Event
}

type AsleepPlan = HashMap<usize, HashMap<usize, Vec<DateTime>>>;

fn create_log(input: &str) -> Vec<LogEntry> {
    let mut result: Vec<LogEntry> = input.lines()
        .filter_map(|line| {
            let mut tokens = line.split(' ');

            let mut date = match tokens.next() {
                Some(x) => x[1..].split('-').filter_map(|x| x.parse::<usize>().ok()),
                _ => return None
            };

            let mut time = match tokens.next() {
                Some(x) => x[..x.len() - 1].split(':').filter_map(|x| x.parse::<usize>().ok()),
                _ => return None
            };

            let event = match (tokens.next(), tokens.next()) {
                (Some("wakes"), _) => Event::WakeUp,
                (Some("falls"), _) => Event::Sleep,
                (Some("Guard"), Some(token)) => match token[1..].parse::<usize>() {
                    Ok(id) => Event::Begin(id),
                    _ => return None
                },
                _ => return None
            };

            match (date.next(), date.next(), date.next(), time.next(), time.next()) {
                (Some(year), Some(month), Some(day), Some(hour), Some(minute)) => {
                    Some(LogEntry {
                        time: DateTime {year, month, day, hour, minute},
                        event
                    })
                },
                _ => None
            }
        })
        .collect();

    result.sort();
    result
}

fn create_plan(log: &Vec<LogEntry>) -> AsleepPlan {
    let mut plan = AsleepPlan::new();

    if log.len() == 0 {
        return plan;
    }

    let mut id = 0;
    let mut last_time = log[0].time;
    let mut asleep = true;

    for &LogEntry {time, event} in log.iter() {
        match event {
            Event::Begin(x) => {
                id = x;

                if !plan.contains_key(&id) {
                    plan.insert(id, HashMap::new());
                }
            },
            Event::WakeUp if asleep => {
                if let Some(asleep_map) = plan.get_mut(&id) {
                    for i in last_time.minute..time.minute {
                        if !asleep_map.contains_key(&i) {
                            asleep_map.insert(i, vec![]);
                        }

                        let asleep_vec = asleep_map.get_mut(&i).unwrap();

                        asleep_vec.push(DateTime {
                            year: time.year,
                            month: time.month,
                            day: time.day,
                            hour: time.hour,
                            minute: i
                        });
                    }
                }

                asleep = false;
            },
            Event::Sleep => {
                last_time = time;
                asleep = true;
            },
            _ => {}
        };
    }

    plan
}

fn main() {
    let data = create_log(&get_input().unwrap());
    let plan = create_plan(&data);

    let (candidate_id, candidate_minute) = plan.iter()
        .max_by_key(|&(_, asleep_map)| asleep_map.values().flatten().count())
        .map(|(&id, asleep_map)| (
            id,
            asleep_map.iter()
                .max_by_key(|&(_, asleep_vec)| asleep_vec.len())
                .map(|(&minute, _)| minute)
                .unwrap_or(0)
        ))
        .unwrap_or((0, 0));

    println!("Part 1: {}", candidate_id * candidate_minute);

    let (candidate_id, candidate_minute) = plan.iter()
        .map(|(&id, asleep_map)| (
            id,
            asleep_map.iter()
                .map(|(&minute, asleep_vec)| (minute, asleep_vec.len()))
                .max_by_key(|&(_, value)| value)
                .unwrap_or((0, 0))
        ))
        .max_by_key(|&(_, (_, value))| value)
        .map(|(x, (y, _))| (x, y))
        .unwrap_or((0, 0));

    println!("Part 2: {}", candidate_id * candidate_minute);
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::collections::{HashSet, HashMap};

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("17.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

type Point = (usize, usize);
type DepthMap = HashSet<Point>;

fn parse(input: &str) -> DepthMap {
    input.lines()
    .flat_map(|line| {
        let parts = line.split(", ")
            .filter_map(|part| {
                let mut tokens = part.split('=');

                match (tokens.next(), tokens.next()) {
                    (Some(coord), Some(value)) => Some((coord, value)),
                    _ => None
                }
            })
            .map(|(coord, value)| (
                coord,
                match value.parse::<usize>() {
                    Ok(x) => vec![x, x],
                    _ => value.split("..").filter_map(|x| x.parse::<usize>().ok()).collect()
                }
            ))
            .filter(|(_, values)| values.len() == 2)
            .fold(HashMap::new(), |mut acc, (coord, values)| {
                acc.insert(coord, values);
                acc
            });

        let (xstart, xend) = parts.get(&"x")
            .map(|v| (v[0], v[1]))
            .unwrap_or((0, 0));

        let (ystart, yend) = parts.get(&"y")
            .map(|v| (v[0], v[1]))
            .unwrap_or((0, 0));

        (xstart..xend + 1)
        .flat_map(move |x| (ystart..yend + 1).map(move |y| (x, y)))
    })
    .collect()
}

fn main() {
    let input = get_input().unwrap();
    let depth_map = parse(&input);

    println!("{:?}", depth_map);
}
<commit_msg>Add 2018 day 17 draft<commit_after>use std::cmp::max;
use std::fs::File;
use std::io::prelude::*;
use std::collections::{HashSet, HashMap};

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("17.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

type Point = (isize, isize);
type DepthMap = HashSet<Point>;

fn parse(input: &str) -> DepthMap {
    input.lines()
    .flat_map(|line| {
        let parts = line.split(", ")
            .filter_map(|part| {
                let mut tokens = part.split('=');

                match (tokens.next(), tokens.next()) {
                    (Some(coord), Some(value)) => Some((coord, value)),
                    _ => None
                }
            })
            .map(|(coord, value)| (
                coord,
                match value.parse::<usize>() {
                    Ok(x) => vec![x, x],
                    _ => value.split("..").filter_map(|x| x.parse::<usize>().ok()).collect()
                }
            ))
            .filter(|(_, values)| values.len() == 2)
            .fold(HashMap::new(), |mut acc, (coord, values)| {
                acc.insert(coord, values);
                acc
            });

        let (xstart, xend) = parts.get(&"x")
            .map(|v| (v[0], v[1]))
            .unwrap_or((0, 0));

        let (ystart, yend) = parts.get(&"y")
            .map(|v| (v[0], v[1]))
            .unwrap_or((0, 0));

        (xstart..xend + 1)
        .flat_map(move |x| (ystart..yend + 1).map(move |y| (x as isize, y as isize)))
    })
    .collect()
}

fn fill_water(source: Point, map: &DepthMap) -> (DepthMap, DepthMap) {
    let miny = map.iter().cloned().map(|(_, y)| y).min().unwrap_or(source.1);
    let maxy = map.iter().cloned().map(|(_, y)| y).max().unwrap_or(source.1);

    let mut water = DepthMap::new();
    let mut flows = DepthMap::new();
    let mut blocked = map.clone();

    fn flows_through((x, y): Point, blocked: &DepthMap) -> bool {
        !blocked.contains(&(x, y + 1))
    }

    fn inner(
        source: Point,
        map: &DepthMap,
        water: &mut DepthMap,
        flows: &mut DepthMap,
        blocked: &mut DepthMap,
        miny: isize,
        maxy: isize
    ) {
        let (sx, sy) = source;

        let (ax, ay) = (sy..maxy + 1)
            .map(|y| (sx, y))
            .find(|&p| !flows_through(p, &blocked))
            .unwrap_or((sx, maxy));

        let ((lx, ly), (rx, ry)) = {
            let left = (0..)
                .map(|x| (ax - x, ay))
                .find(|&(x, y)| {
                    blocked.contains(&(x - 1, y))
                    || flows_through((x, y), &blocked)
                })
                .unwrap();

            let right = (0..)
                .map(|x| (ax + x, ay))
                .find(|&(x, y)| {
                    blocked.contains(&(x + 1, y))
                    || flows_through((x, y), &blocked)
                })
                .unwrap();

            (left, right)
        };

        // Update flows & water

        let left_overflow = flows_through((lx, ly), &blocked);
        let right_overflow = flows_through((rx, ry), &blocked);
        let overflow = left_overflow || right_overflow;
        let top = (lx..rx + 1).map(|x| (x, ay)).collect::<DepthMap>();

        let old_water_count = water.len();
        let old_flow_count = flows.len();

        println!("{:?} {:?} {:?}: {:?}, {:?}, {:?} - {}, {}", source, left_overflow, right_overflow, (lx, ly), (ax, ay), (rx, ry), water.len(), flows.len());

        if !overflow {
            *water = water.union(&top).cloned().collect();
            *blocked = blocked.union(&top).cloned().collect();
        } else {
            *flows = flows
                .union(&top)
                .cloned()
                .chain(
                    if !flows.contains(&(sx, sy + 1)) {
                        (max(sy, miny)..ay + 1)
                    } else {
                        (0..0)
                    }
                    .map(|y| (sx, y))
                )
                .collect();
        }

        // Propagate

        let (left_source, right_source) = (
            left_overflow && (lx, ly) != (ax, ay),
            right_overflow && (rx, ry) != (ax, ay)
        );

        if left_source {
            inner((lx, ly), map, water, flows, blocked, miny, maxy);
        }

        if right_source {
            inner((rx, ry), map, water, flows, blocked, miny, maxy);
        }

        if overflow && old_flow_count == flows.len() && old_water_count == water.len() {
            return;
        }

        if !overflow {
            inner(source, map, water, flows, blocked, miny, maxy);
        }
    }

    inner(source, map, &mut water, &mut flows, &mut blocked, miny, maxy);

    (water, flows)
}

fn main() {
    let input = get_input().unwrap();
    let depth_map = parse(&input);
    let (water, flows) = fill_water((500, 0), &depth_map);

    println!("Part 1: {}", water.union(&flows).count());
}
<|endoftext|>


<commit_before>mod common;

use std::env::{self, VarError};
use std::mem::size_of;
use std::process::{exit, Child, Command, ExitStatus};
use std::thread;
use std::time::Duration;

use rand::Rng;

use sled::Config;

use common::cleanup;

const TEST_ENV_VAR: &str = "SLED_CRASH_TEST";
const N_TESTS: usize = 100;
const CYCLE: usize = 256;
const BATCH_SIZE: u32 = 8;
const SEGMENT_SIZE: usize = 1024;

// test names, also used as dir names
const RECOVERY_NO_SNAPSHOT: &str = "crash_recovery_no_runtime_snapshot";
const BATCHES_NO_SNAPSHOT: &str = "crash_batches_no_runtime_snapshot";

fn main() {
    common::setup_logger();

    match env::var(TEST_ENV_VAR) {
        Err(VarError::NotPresent) => {
            test_crash_recovery();

            // TODO this is currently being ignored until it is fixed
            // with a refactor of recovery logic.
            test_crash_batches();
        }

        Ok(ref s) if s == RECOVERY_NO_SNAPSHOT => run(s),
        Ok(ref s) if s == BATCHES_NO_SNAPSHOT => run_batches(s),

        Ok(_) | Err(_) => panic!("invalid crash test case"),
    }
}

/// Verifies that the keys in the tree are correctly recovered.
/// Panics if they are incorrect.
/// Returns the key that should be resumed at, and the current cycle value.
fn verify(tree: &sled::Tree) -> (u32, u32) {
    // key 0 should always be the highest value, as that's where we increment
    // at some point, it might go down by one
    // it should never return, or go down again after that
    let mut iter = tree.iter();
    let highest = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return (0, 0),
    };

    let highest_vec = u32_to_vec(highest);

    // find how far we got
    let mut contiguous: u32 = 0;
    let mut lowest = 0;
    for res in iter {
        let (_k, v) = res.unwrap();
        if &v[..4] == &highest_vec[..4] {
            contiguous += 1;
        } else {
            let expected = if highest == 0 {
                CYCLE as u32 - 1
            } else {
                (highest - 1) % CYCLE as u32
            };
            let actual = slice_to_u32(&*v);
            assert_eq!(expected, actual);
            lowest = actual;
            break;
        }
    }

    // ensure nothing changes after this point
    let low_beginning = u32_to_vec(contiguous + 1);

    for res in tree.range(&*low_beginning..) {
        let (k, v): (sled::IVec, _) = res.unwrap();
        assert_eq!(
            slice_to_u32(&*v),
            lowest,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            slice_to_u32(&*k),
            lowest,
            slice_to_u32(&*v),
            tree
        );
    }

    (contiguous, highest)
}

fn u32_to_vec(u: u32) -> Vec<u8> {
    let buf: [u8; size_of::<u32>()] = u.to_be_bytes();
    buf.to_vec()
}

fn slice_to_u32(b: &[u8]) -> u32 {
    let mut buf = [0u8; size_of::<u32>()];
    buf.copy_from_slice(&b[..size_of::<u32>()]);

    u32::from_be_bytes(buf)
}

fn spawn_killah() {
    thread::spawn(|| {
        let runtime = rand::thread_rng().gen_range(0, 200);
        thread::sleep(Duration::from_millis(runtime));
        println!("~");
        exit(9);
    });
}

fn run_inner(config: Config) {
    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    if crash_during_initialization {
        spawn_killah();
    }

    let tree = config.open().unwrap();

    if !crash_during_initialization {
        spawn_killah();
    }

    let (key, highest) = verify(&tree);

    let mut hu = ((highest as usize) * CYCLE) + key as usize;
    assert_eq!(hu % CYCLE, key as usize);
    assert_eq!(hu / CYCLE, highest as usize);

    loop {
        hu += 1;

        if hu / CYCLE >= CYCLE {
            hu = 0;
        }

        let key = u32_to_vec((hu % CYCLE) as u32);

        let mut value = u32_to_vec((hu / CYCLE) as u32);
        let additional_len = rand::thread_rng().gen_range(0, SEGMENT_SIZE / 3);
        value.append(&mut vec![0u8; additional_len]);

        tree.insert(&key, value).unwrap();
    }
}

/// Verifies that the keys in the tree are correctly recovered (i.e., equal).
/// Panics if they are incorrect.
fn verify_batches(tree: &sled::Tree) -> u32 {
    let mut iter = tree.iter();
    let first_value = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return 0,
    };
    for key in 0..BATCH_SIZE {
        let res = tree.get(u32_to_vec(key));
        let option = res.unwrap();
        let v = match option {
            Some(v) => v,
            None => panic!(
                "expected key {} to have a value, instead it was missing in db: {:?}",
                key, tree
            ),
        };
        let value = slice_to_u32(&*v);
        assert_eq!(
            first_value, value,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            key, first_value, value, tree
        );
    }

    first_value
}

fn run_batches_inner(db: sled::Db) {
    fn do_batch(i: u32, db: &sled::Db) {
        let mut rng = rand::thread_rng();
        let base_value = u32_to_vec(i);

        let mut batch = sled::Batch::default();
        if rng.gen_bool(0.1) {
            for key in 0..BATCH_SIZE {
                batch.remove(u32_to_vec(key));
            }
        } else {
            for key in 0..BATCH_SIZE {
                let mut value = base_value.clone();
                let additional_len = rng.gen_range(0, SEGMENT_SIZE / 3);
                value.append(&mut vec![0u8; additional_len]);

                batch.insert(u32_to_vec(key), value);
            }
        }
        db.apply_batch(batch).unwrap();
    }

    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    if crash_during_initialization {
        spawn_killah();
    }

    let mut i = verify_batches(&db);
    i += 1;
    do_batch(i, &db);

    if !crash_during_initialization {
        spawn_killah();
    }

    loop {
        i += 1;
        do_batch(i, &db);
    }
}

fn run(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(1))
        .path(dir.to_string())
        .segment_size(SEGMENT_SIZE);

    match thread::spawn(|| run_inner(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_batches(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(1))
        .path(dir.to_string())
        .segment_size(SEGMENT_SIZE);

    let db = config.open().unwrap();
    // let db2 = db.clone();

    let t1 = thread::spawn(|| run_batches_inner(db));
    let t2 = thread::spawn(|| {}); // run_batches_inner(db2));
    match t1.join().and_then(|_| t2.join()) {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_child_process(test_name: &str) -> Child {
    let bin = env::current_exe().expect("could not get test binary path");

    env::set_var(TEST_ENV_VAR, test_name);

    Command::new(bin).env(TEST_ENV_VAR, test_name).spawn().expect(&format!(
        "could not spawn child process for {} test",
        test_name
    ))
}

fn handle_child_exit_status(dir: &str, status: ExitStatus) {
    let code = status.code();

    if code.is_none() || code.unwrap() != 9 {
        cleanup(dir);
        panic!("{} test child exited abnormally", dir);
    }
}

fn handle_child_wait_err(dir: &str, e: std::io::Error) {
    cleanup(dir);

    panic!("error waiting for {} test child: {}", dir, e);
}

fn test_crash_recovery() {
    let dir = RECOVERY_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(RECOVERY_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}

#[allow(dead_code)]
fn test_crash_batches() {
    let dir = BATCHES_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(BATCHES_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}
<commit_msg>Kill the processe under test after up to 20ms instead of 200<commit_after>mod common;

use std::env::{self, VarError};
use std::mem::size_of;
use std::process::{exit, Child, Command, ExitStatus};
use std::thread;
use std::time::Duration;

use rand::Rng;

use sled::Config;

use common::cleanup;

const TEST_ENV_VAR: &str = "SLED_CRASH_TEST";
const N_TESTS: usize = 100;
const CYCLE: usize = 256;
const BATCH_SIZE: u32 = 8;
const SEGMENT_SIZE: usize = 1024;

// test names, also used as dir names
const RECOVERY_NO_SNAPSHOT: &str = "crash_recovery_no_runtime_snapshot";
const BATCHES_NO_SNAPSHOT: &str = "crash_batches_no_runtime_snapshot";

fn main() {
    common::setup_logger();

    match env::var(TEST_ENV_VAR) {
        Err(VarError::NotPresent) => {
            test_crash_recovery();

            // TODO this is currently being ignored until it is fixed
            // with a refactor of recovery logic.
            test_crash_batches();
        }

        Ok(ref s) if s == RECOVERY_NO_SNAPSHOT => run(s),
        Ok(ref s) if s == BATCHES_NO_SNAPSHOT => run_batches(s),

        Ok(_) | Err(_) => panic!("invalid crash test case"),
    }
}

/// Verifies that the keys in the tree are correctly recovered.
/// Panics if they are incorrect.
/// Returns the key that should be resumed at, and the current cycle value.
fn verify(tree: &sled::Tree) -> (u32, u32) {
    // key 0 should always be the highest value, as that's where we increment
    // at some point, it might go down by one
    // it should never return, or go down again after that
    let mut iter = tree.iter();
    let highest = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return (0, 0),
    };

    let highest_vec = u32_to_vec(highest);

    // find how far we got
    let mut contiguous: u32 = 0;
    let mut lowest = 0;
    for res in iter {
        let (_k, v) = res.unwrap();
        if &v[..4] == &highest_vec[..4] {
            contiguous += 1;
        } else {
            let expected = if highest == 0 {
                CYCLE as u32 - 1
            } else {
                (highest - 1) % CYCLE as u32
            };
            let actual = slice_to_u32(&*v);
            assert_eq!(expected, actual);
            lowest = actual;
            break;
        }
    }

    // ensure nothing changes after this point
    let low_beginning = u32_to_vec(contiguous + 1);

    for res in tree.range(&*low_beginning..) {
        let (k, v): (sled::IVec, _) = res.unwrap();
        assert_eq!(
            slice_to_u32(&*v),
            lowest,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            slice_to_u32(&*k),
            lowest,
            slice_to_u32(&*v),
            tree
        );
    }

    (contiguous, highest)
}

fn u32_to_vec(u: u32) -> Vec<u8> {
    let buf: [u8; size_of::<u32>()] = u.to_be_bytes();
    buf.to_vec()
}

fn slice_to_u32(b: &[u8]) -> u32 {
    let mut buf = [0u8; size_of::<u32>()];
    buf.copy_from_slice(&b[..size_of::<u32>()]);

    u32::from_be_bytes(buf)
}

fn spawn_killah() {
    thread::spawn(|| {
        let runtime = rand::thread_rng().gen_range(0, 20);
        thread::sleep(Duration::from_millis(runtime));
        println!("~");
        exit(9);
    });
}

fn run_inner(config: Config) {
    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    if crash_during_initialization {
        spawn_killah();
    }

    let tree = config.open().unwrap();

    if !crash_during_initialization {
        spawn_killah();
    }

    let (key, highest) = verify(&tree);

    let mut hu = ((highest as usize) * CYCLE) + key as usize;
    assert_eq!(hu % CYCLE, key as usize);
    assert_eq!(hu / CYCLE, highest as usize);

    loop {
        hu += 1;

        if hu / CYCLE >= CYCLE {
            hu = 0;
        }

        let key = u32_to_vec((hu % CYCLE) as u32);

        let mut value = u32_to_vec((hu / CYCLE) as u32);
        let additional_len = rand::thread_rng().gen_range(0, SEGMENT_SIZE / 3);
        value.append(&mut vec![0u8; additional_len]);

        tree.insert(&key, value).unwrap();
    }
}

/// Verifies that the keys in the tree are correctly recovered (i.e., equal).
/// Panics if they are incorrect.
fn verify_batches(tree: &sled::Tree) -> u32 {
    let mut iter = tree.iter();
    let first_value = match iter.next() {
        Some(Ok((_k, v))) => slice_to_u32(&*v),
        Some(Err(e)) => panic!("{:?}", e),
        None => return 0,
    };
    for key in 0..BATCH_SIZE {
        let res = tree.get(u32_to_vec(key));
        let option = res.unwrap();
        let v = match option {
            Some(v) => v,
            None => panic!(
                "expected key {} to have a value, instead it was missing in db: {:?}",
                key, tree
            ),
        };
        let value = slice_to_u32(&*v);
        assert_eq!(
            first_value, value,
            "expected key {} to have value {}, instead it had value {} in db: {:?}",
            key, first_value, value, tree
        );
    }

    first_value
}

fn run_batches_inner(db: sled::Db) {
    fn do_batch(i: u32, db: &sled::Db) {
        let mut rng = rand::thread_rng();
        let base_value = u32_to_vec(i);

        let mut batch = sled::Batch::default();
        if rng.gen_bool(0.1) {
            for key in 0..BATCH_SIZE {
                batch.remove(u32_to_vec(key));
            }
        } else {
            for key in 0..BATCH_SIZE {
                let mut value = base_value.clone();
                let additional_len = rng.gen_range(0, SEGMENT_SIZE / 3);
                value.append(&mut vec![0u8; additional_len]);

                batch.insert(u32_to_vec(key), value);
            }
        }
        db.apply_batch(batch).unwrap();
    }

    let crash_during_initialization = rand::thread_rng().gen_bool(0.1);

    if crash_during_initialization {
        spawn_killah();
    }

    let mut i = verify_batches(&db);
    i += 1;
    do_batch(i, &db);

    if !crash_during_initialization {
        spawn_killah();
    }

    loop {
        i += 1;
        do_batch(i, &db);
    }
}

fn run(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(1))
        .path(dir.to_string())
        .segment_size(SEGMENT_SIZE);

    match thread::spawn(|| run_inner(config)).join() {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_batches(dir: &str) {
    let config = Config::new()
        .cache_capacity(128 * 1024 * 1024)
        .flush_every_ms(Some(1))
        .path(dir.to_string())
        .segment_size(SEGMENT_SIZE);

    let db = config.open().unwrap();
    // let db2 = db.clone();

    let t1 = thread::spawn(|| run_batches_inner(db));
    let t2 = thread::spawn(|| {}); // run_batches_inner(db2));
    match t1.join().and_then(|_| t2.join()) {
        Err(e) => {
            println!("worker thread failed: {:?}", e);
            std::process::exit(15);
        }
        _ => {}
    }
}

fn run_child_process(test_name: &str) -> Child {
    let bin = env::current_exe().expect("could not get test binary path");

    env::set_var(TEST_ENV_VAR, test_name);

    Command::new(bin).env(TEST_ENV_VAR, test_name).spawn().expect(&format!(
        "could not spawn child process for {} test",
        test_name
    ))
}

fn handle_child_exit_status(dir: &str, status: ExitStatus) {
    let code = status.code();

    if code.is_none() || code.unwrap() != 9 {
        cleanup(dir);
        panic!("{} test child exited abnormally", dir);
    }
}

fn handle_child_wait_err(dir: &str, e: std::io::Error) {
    cleanup(dir);

    panic!("error waiting for {} test child: {}", dir, e);
}

fn test_crash_recovery() {
    let dir = RECOVERY_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(RECOVERY_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}

#[allow(dead_code)]
fn test_crash_batches() {
    let dir = BATCHES_NO_SNAPSHOT;
    cleanup(dir);

    for _ in 0..N_TESTS {
        let mut child = run_child_process(BATCHES_NO_SNAPSHOT);

        child
            .wait()
            .map(|status| handle_child_exit_status(dir, status))
            .map_err(|e| handle_child_wait_err(dir, e))
            .unwrap();
    }

    cleanup(dir);
}
<|endoftext|>


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

#[crate_id = "github.com/mozilla-servo/rust-core-graphics#core_graphics:0.1"];

extern crate std;
extern crate core_foundation;

pub mod base;
pub mod data_provider;
pub mod font;
pub mod geometry;
<commit_msg>Generate both dylib and rlib for rustup<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[crate_id = "github.com/mozilla-servo/rust-core-graphics#core_graphics:0.1"];
#[crate_type = "lib"];
#[crate_type = "dylib"];
#[crate_type = "rlib"];

extern crate std;
extern crate core_foundation;

pub mod base;
pub mod data_provider;
pub mod font;
pub mod geometry;
<|endoftext|>


<commit_before>//
// sprocketnes/ppu.rs
//
// Copyright (c) 2013 Mozilla Foundation
// Author: Patrick Walton
//

use mem::Mem;
use rom::Rom;
use util::{debug_assert, debug_print};

use core::uint::range;

//
// Constants
//

pub const SCREEN_WIDTH: uint = 256;
pub const SCREEN_HEIGHT: uint = 240;
pub const CYCLES_PER_SCANLINE: u64 = 124;   // 29781 cycles per frame, 240 scanlines
pub const VBLANK_SCANLINE: uint = 241;
pub const LAST_SCANLINE: uint = 261;

//
// Registers
//

struct Regs {
    ctrl: PpuCtrl,      // PPUCTRL: 0x2000
    mask: PpuMask,      // PPUMASK: 0x2001
    status: PpuStatus,  // PPUSTATUS: 0x2002
    oam_addr: u8,       // OAMADDR: 0x2003
    scroll: PpuScroll,  // PPUSCROLL: 0x2005
    addr: u16,          // PPUADDR: 0x2006
}

//
// PPUCTRL: 0x2000
//

struct PpuCtrl(u8);

enum SpriteSize {
    SpriteSize8x8,
    SpriteSize8x16
}

impl PpuCtrl {
    fn base_nametable_addr(self) -> u16           { 0x2000 + (*self & 0x3) as u16 * 0x400 }
    fn vram_addr_increment(self) -> u16           { if (*self & 0x04) == 0 { 1 } else { 32 } }
    fn sprite_pattern_table_addr(self) -> u16     { if (*self & 0x08) == 0 { 0 } else { 0x1000 } }
    fn background_pattern_table_addr(self) -> u16 { if (*self & 0x10) == 0 { 0 } else { 0x1000 } }
    fn sprite_size(self) -> SpriteSize {
        if (*self & 0x20) == 0 { SpriteSize8x8 } else { SpriteSize8x16 }
    }
    fn vblank_nmi(self) -> bool                   { (*self & 0x80) != 0 }
}

//
// PPUMASK: 0x2001
//

struct PpuMask(u8);

impl PpuMask {
    fn grayscale(self) -> bool               { (*self & 0x01) != 0 }
    fn show_background_on_left(self) -> bool { (*self & 0x02) != 0 }
    fn show_sprites_on_left(self) -> bool    { (*self & 0x04) != 0 }
    fn show_background(self) -> bool         { (*self & 0x08) != 0 }
    fn show_sprites(self) -> bool            { (*self & 0x10) != 0 }
    fn intensify_reds(self) -> bool          { (*self & 0x20) != 0 }
    fn intensify_greens(self) -> bool        { (*self & 0x40) != 0 }
    fn intensity_blues(self) -> bool         { (*self & 0x80) != 0 }
}

//
// PPUSTATUS: 0x2002
//

struct PpuStatus(u8);

impl PpuStatus {
    // TODO: open bus junk in bits [0,5)
    fn set_sprite_overflow(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x20) } else { *self = PpuStatus(**self & !0x20) }
    }
    fn set_sprite_zero_hit(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x40) } else { *self = PpuStatus(**self & !0x40) }
    }
    fn set_in_vblank(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x80) } else { *self = PpuStatus(**self & !0x80) }
    }
}

//
// PPUSCROLL: 0x2005
//

struct PpuScroll {
    x: u8,
    y: u8,
    next: PpuScrollDir
}

enum PpuScrollDir {
    XDir,
    YDir,
}

// PPU VRAM. This implements the same Mem trait that the CPU memory does.

pub struct Vram {
    rom: &Rom,
    nametables: [u8 * 0x1000],  // 4 nametables, 0x400 each
    palette: [u8 * 0x20],
}

pub impl Vram {
    static fn new(rom: &a/Rom) -> Vram/&a {
        Vram {
            rom: rom,
            nametables: [ 0, ..0x1000 ],
            palette: [ 0, ..0x20 ]
        }
    }
}

pub impl Vram : Mem {
    fn loadb(&mut self, addr: u16) -> u8 {
        if addr < 0x2000 {          // Tilesets 0 or 1
            return self.rom.chr[addr]
        }
        if addr < 0x3f00 {          // Name table area
            let addr = addr & 0x0fff;
            return self.nametables[addr]
        }
        if addr < 0x4000 {          // Palette area
            let addr = addr & 0x1f;
            return self.palette[addr]
        }
        fail ~"invalid VRAM read"
    }
    fn storeb(&mut self, addr: u16, val: u8) {
        if addr < 0x2000 {
            return                  // Attempt to write to CHR-ROM; ignore.
        }
        if addr < 0x3f00 {          // Name table area
            let addr = addr & 0x0fff;
            self.nametables[addr] = val;
        } else if addr < 0x4000 {   // Palette area
            let addr = addr & 0x1f;
            self.palette[addr] = val;
        }
    }
}

// Object Attribute Memory (OAM).

pub struct Oam {
    oam: [u8 * 0x100]
}

pub impl Oam {
    static fn new() -> Oam {
        Oam { oam: [ 0, ..0x100 ] }
    }
}

pub impl Oam : Mem {
    fn loadb(&mut self, addr: u16) -> u8     { self.oam[addr] }
    fn storeb(&mut self, addr: u16, val: u8) { self.oam[addr] = val }
}

// The main PPU structure. This structure is separate from the PPU memory just as the CPU is.

pub struct Ppu<VM,OM> {
    regs: Regs,
    vram: VM,
    oam: OM,

    screen: ~([u8 * 184320]),  // 256 * 240 * 3
    scanline: u16,
    cy: u64
}

pub impl<VM:Mem,OM:Mem> Ppu<VM,OM> : Mem {
    // Performs a load of the PPU register at the given CPU address.
    fn loadb(&mut self, addr: u16) -> u8 {
        debug_assert(addr >= 0x2000 && addr < 0x4000, "invalid PPU register");
        match addr & 7 {
            0 => *self.regs.ctrl,
            1 => *self.regs.mask,
            2 => *self.regs.status,
            3 => 0, // OAMADDR is read-only
            4 => fail ~"OAM read unimplemented",
            5 => 0, // PPUSCROLL is read-only
            6 => 0, // PPUADDR is read-only
            7 => self.read_ppudata(),
            _ => fail ~"can't happen"
        }
    }

    // Performs a store to the PPU register at the given CPU address.
    fn storeb(&mut self, addr: u16, val: u8) {
        debug_assert(addr >= 0x2000 && addr < 0x4000, "invalid PPU register");
        match addr & 7 {
            0 => self.regs.ctrl = PpuCtrl(val),
            1 => self.regs.mask = PpuMask(val),
            2 => (),    // PPUSTATUS is read-only
            3 => self.regs.oam_addr = val,
            4 => fail ~"OAM write unimplemented",
            5 => self.update_ppuscroll(val),
            6 => self.update_ppuaddr(val),
            7 => self.write_ppudata(val),
            _ => fail ~"can't happen"
        }
    }
}

#[deriving_eq]
pub struct StepResult {
    new_frame: bool,    // We wrapped around to the next scanline.
    vblank_nmi: bool,   // We entered VBLANK and must generate an NMI.
}

pub impl<VM:Mem,OM:Mem> Ppu<VM,OM> {
    static fn new(vram: VM, oam: OM) -> Ppu<VM,OM> {
        Ppu {
            regs: Regs {
                ctrl: PpuCtrl(0),
                mask: PpuMask(0),
                status: PpuStatus(0),
                oam_addr: 0,
                scroll: PpuScroll { x: 0, y: 0, next: XDir },
                addr: 0,
            },
            vram: vram,
            oam: oam,

            screen: ~([ 0, ..184320 ]),
            scanline: 0,
            cy: 0
        }
    }

    //
    // Register manipulation
    //

    fn update_ppuscroll(&mut self, val: u8) {
        match self.regs.scroll.next {
            XDir => {
                self.regs.scroll.x = val;
                self.regs.scroll.next = YDir;
            }
            YDir => {
                self.regs.scroll.y = val;
                self.regs.scroll.next = XDir;
            }
        }
    }

    fn update_ppuaddr(&mut self, val: u8) {
        self.regs.addr = (self.regs.addr << 8) | (val as u16);
    }

    fn write_ppudata(&mut self, val: u8) {
        self.vram.storeb(self.regs.addr, val);
        self.regs.addr += self.regs.ctrl.vram_addr_increment();
    }

    fn read_ppudata(&mut self) -> u8 {
        let val = self.vram.loadb(self.regs.addr);
        self.regs.addr += self.regs.ctrl.vram_addr_increment();
        val
    }

    //
    // Rendering
    //

    fn putpixel(&mut self, x: uint, y: uint, color: u32) {
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 0] = (color >> 24) as u8;
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 1] = (color >> 16) as u8;
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 2] = (color >> 8) as u8;
    }

    fn render_scanline(&mut self) {
        // TODO: Scrolling, mirroring
        let mut nametable_offset = 0x2000 + 32 * (self.scanline / 8);
        for range(0, SCREEN_WIDTH) |x| {
            // TODO: Sprites, tiles

            // FIXME: For performance, we shouldn't be recomputing the tile for every pixel.
            let tile = self.vram.loadb(nametable_offset + (x as u16 / 8)) as u32;

            // TODO: Right pattern table
            let pattern_offset = (tile << 4) as u16 + (self.scanline as u16) % 8;
            let plane0 = self.vram.loadb(pattern_offset);
            let plane1 = self.vram.loadb(pattern_offset + 8);
            let bit0 = (plane0 >> (7 - ((x % 8) as u8))) & 1;
            let bit1 = (plane1 >> (7 - ((x % 8) as u8))) & 1;
            let color = (bit1 << 1) | bit0;

            //let r = (tile & 0xc0);
            //let g = (tile & 0x38) << 2;
            //let b = (tile & 0x03) << 5;
            let (r, g, b) = ((color << 6) as u32, (color << 6) as u32, (color << 6) as u32);
            self.putpixel(x, self.scanline as uint, (r << 8) | (g << 16) | (b << 24));
        }
    }

    fn start_vblank(&mut self, result: &mut StepResult) {
        self.regs.status.set_in_vblank(true);

        if self.regs.ctrl.vblank_nmi() {
            debug_print("VBLANK NMI!");
            result.vblank_nmi = true;
        }
    }

    fn step(&mut self, run_to_cycle: u64) -> StepResult {
        let mut result = StepResult { new_frame: false, vblank_nmi: false };
        loop {
            let next_scanline_cycle: u64 = self.cy + CYCLES_PER_SCANLINE;
            if next_scanline_cycle > run_to_cycle {
                break;
            }

            if self.scanline < (SCREEN_HEIGHT as u16) {
                self.render_scanline();
            }

            self.scanline += 1;
            if self.scanline == (VBLANK_SCANLINE as u16) {
                self.start_vblank(&mut result);
            } else if self.scanline == (LAST_SCANLINE as u16) {
                result.new_frame = true;
                self.scanline = 0;
                self.regs.status.set_in_vblank(false);
            }

            self.cy += CYCLES_PER_SCANLINE;

            debug_assert(self.cy % CYCLES_PER_SCANLINE == 0, "at even scanline cycle");
        }

        return result;
    }
}

<commit_msg>Add a few inlines to optimize blitting<commit_after>//
// sprocketnes/ppu.rs
//
// Copyright (c) 2013 Mozilla Foundation
// Author: Patrick Walton
//

use mem::Mem;
use rom::Rom;
use util::{debug_assert, debug_print};

use core::uint::range;

//
// Constants
//

pub const SCREEN_WIDTH: uint = 256;
pub const SCREEN_HEIGHT: uint = 240;
pub const CYCLES_PER_SCANLINE: u64 = 124;   // 29781 cycles per frame, 240 scanlines
pub const VBLANK_SCANLINE: uint = 241;
pub const LAST_SCANLINE: uint = 261;

//
// Registers
//

struct Regs {
    ctrl: PpuCtrl,      // PPUCTRL: 0x2000
    mask: PpuMask,      // PPUMASK: 0x2001
    status: PpuStatus,  // PPUSTATUS: 0x2002
    oam_addr: u8,       // OAMADDR: 0x2003
    scroll: PpuScroll,  // PPUSCROLL: 0x2005
    addr: u16,          // PPUADDR: 0x2006
}

//
// PPUCTRL: 0x2000
//

struct PpuCtrl(u8);

enum SpriteSize {
    SpriteSize8x8,
    SpriteSize8x16
}

impl PpuCtrl {
    fn base_nametable_addr(self) -> u16           { 0x2000 + (*self & 0x3) as u16 * 0x400 }
    fn vram_addr_increment(self) -> u16           { if (*self & 0x04) == 0 { 1 } else { 32 } }
    fn sprite_pattern_table_addr(self) -> u16     { if (*self & 0x08) == 0 { 0 } else { 0x1000 } }
    fn background_pattern_table_addr(self) -> u16 { if (*self & 0x10) == 0 { 0 } else { 0x1000 } }
    fn sprite_size(self) -> SpriteSize {
        if (*self & 0x20) == 0 { SpriteSize8x8 } else { SpriteSize8x16 }
    }
    fn vblank_nmi(self) -> bool                   { (*self & 0x80) != 0 }
}

//
// PPUMASK: 0x2001
//

struct PpuMask(u8);

impl PpuMask {
    fn grayscale(self) -> bool               { (*self & 0x01) != 0 }
    fn show_background_on_left(self) -> bool { (*self & 0x02) != 0 }
    fn show_sprites_on_left(self) -> bool    { (*self & 0x04) != 0 }
    fn show_background(self) -> bool         { (*self & 0x08) != 0 }
    fn show_sprites(self) -> bool            { (*self & 0x10) != 0 }
    fn intensify_reds(self) -> bool          { (*self & 0x20) != 0 }
    fn intensify_greens(self) -> bool        { (*self & 0x40) != 0 }
    fn intensity_blues(self) -> bool         { (*self & 0x80) != 0 }
}

//
// PPUSTATUS: 0x2002
//

struct PpuStatus(u8);

impl PpuStatus {
    // TODO: open bus junk in bits [0,5)
    fn set_sprite_overflow(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x20) } else { *self = PpuStatus(**self & !0x20) }
    }
    fn set_sprite_zero_hit(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x40) } else { *self = PpuStatus(**self & !0x40) }
    }
    fn set_in_vblank(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x80) } else { *self = PpuStatus(**self & !0x80) }
    }
}

//
// PPUSCROLL: 0x2005
//

struct PpuScroll {
    x: u8,
    y: u8,
    next: PpuScrollDir
}

enum PpuScrollDir {
    XDir,
    YDir,
}

// PPU VRAM. This implements the same Mem trait that the CPU memory does.

pub struct Vram {
    rom: &Rom,
    nametables: [u8 * 0x1000],  // 4 nametables, 0x400 each
    palette: [u8 * 0x20],
}

pub impl Vram {
    static fn new(rom: &a/Rom) -> Vram/&a {
        Vram {
            rom: rom,
            nametables: [ 0, ..0x1000 ],
            palette: [ 0, ..0x20 ]
        }
    }
}

pub impl Vram : Mem {
    #[inline(always)]
    fn loadb(&mut self, addr: u16) -> u8 {
        if addr < 0x2000 {          // Tilesets 0 or 1
            return self.rom.chr[addr]
        }
        if addr < 0x3f00 {          // Name table area
            let addr = addr & 0x0fff;
            return self.nametables[addr]
        }
        if addr < 0x4000 {          // Palette area
            let addr = addr & 0x1f;
            return self.palette[addr]
        }
        fail ~"invalid VRAM read"
    }
    fn storeb(&mut self, addr: u16, val: u8) {
        if addr < 0x2000 {
            return                  // Attempt to write to CHR-ROM; ignore.
        }
        if addr < 0x3f00 {          // Name table area
            let addr = addr & 0x0fff;
            self.nametables[addr] = val;
        } else if addr < 0x4000 {   // Palette area
            let addr = addr & 0x1f;
            self.palette[addr] = val;
        }
    }
}

// Object Attribute Memory (OAM).

pub struct Oam {
    oam: [u8 * 0x100]
}

pub impl Oam {
    static fn new() -> Oam {
        Oam { oam: [ 0, ..0x100 ] }
    }
}

pub impl Oam : Mem {
    fn loadb(&mut self, addr: u16) -> u8     { self.oam[addr] }
    fn storeb(&mut self, addr: u16, val: u8) { self.oam[addr] = val }
}

// The main PPU structure. This structure is separate from the PPU memory just as the CPU is.

pub struct Ppu<VM,OM> {
    regs: Regs,
    vram: VM,
    oam: OM,

    screen: ~([u8 * 184320]),  // 256 * 240 * 3
    scanline: u16,
    cy: u64
}

pub impl<VM:Mem,OM:Mem> Ppu<VM,OM> : Mem {
    // Performs a load of the PPU register at the given CPU address.
    fn loadb(&mut self, addr: u16) -> u8 {
        debug_assert(addr >= 0x2000 && addr < 0x4000, "invalid PPU register");
        match addr & 7 {
            0 => *self.regs.ctrl,
            1 => *self.regs.mask,
            2 => *self.regs.status,
            3 => 0, // OAMADDR is read-only
            4 => fail ~"OAM read unimplemented",
            5 => 0, // PPUSCROLL is read-only
            6 => 0, // PPUADDR is read-only
            7 => self.read_ppudata(),
            _ => fail ~"can't happen"
        }
    }

    // Performs a store to the PPU register at the given CPU address.
    fn storeb(&mut self, addr: u16, val: u8) {
        debug_assert(addr >= 0x2000 && addr < 0x4000, "invalid PPU register");
        match addr & 7 {
            0 => self.regs.ctrl = PpuCtrl(val),
            1 => self.regs.mask = PpuMask(val),
            2 => (),    // PPUSTATUS is read-only
            3 => self.regs.oam_addr = val,
            4 => fail ~"OAM write unimplemented",
            5 => self.update_ppuscroll(val),
            6 => self.update_ppuaddr(val),
            7 => self.write_ppudata(val),
            _ => fail ~"can't happen"
        }
    }
}

#[deriving_eq]
pub struct StepResult {
    new_frame: bool,    // We wrapped around to the next scanline.
    vblank_nmi: bool,   // We entered VBLANK and must generate an NMI.
}

pub impl<VM:Mem,OM:Mem> Ppu<VM,OM> {
    static fn new(vram: VM, oam: OM) -> Ppu<VM,OM> {
        Ppu {
            regs: Regs {
                ctrl: PpuCtrl(0),
                mask: PpuMask(0),
                status: PpuStatus(0),
                oam_addr: 0,
                scroll: PpuScroll { x: 0, y: 0, next: XDir },
                addr: 0,
            },
            vram: vram,
            oam: oam,

            screen: ~([ 0, ..184320 ]),
            scanline: 0,
            cy: 0
        }
    }

    //
    // Register manipulation
    //

    fn update_ppuscroll(&mut self, val: u8) {
        match self.regs.scroll.next {
            XDir => {
                self.regs.scroll.x = val;
                self.regs.scroll.next = YDir;
            }
            YDir => {
                self.regs.scroll.y = val;
                self.regs.scroll.next = XDir;
            }
        }
    }

    fn update_ppuaddr(&mut self, val: u8) {
        self.regs.addr = (self.regs.addr << 8) | (val as u16);
    }

    fn write_ppudata(&mut self, val: u8) {
        self.vram.storeb(self.regs.addr, val);
        self.regs.addr += self.regs.ctrl.vram_addr_increment();
    }

    fn read_ppudata(&mut self) -> u8 {
        let val = self.vram.loadb(self.regs.addr);
        self.regs.addr += self.regs.ctrl.vram_addr_increment();
        val
    }

    //
    // Rendering
    //

    #[inline(always)]
    fn putpixel(&mut self, x: uint, y: uint, color: u32) {
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 0] = (color >> 24) as u8;
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 1] = (color >> 16) as u8;
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 2] = (color >> 8) as u8;
    }

    fn render_scanline(&mut self) {
        // TODO: Scrolling, mirroring
        let mut nametable_offset = 0x2000 + 32 * (self.scanline / 8);
        for range(0, SCREEN_WIDTH) |x| {
            // TODO: Sprites, tiles

            // FIXME: For performance, we shouldn't be recomputing the tile for every pixel.
            let tile = self.vram.loadb(nametable_offset + (x as u16 / 8)) as u32;

            // TODO: Right pattern table
            let pattern_offset = (tile << 4) as u16 + (self.scanline as u16) % 8;
            let plane0 = self.vram.loadb(pattern_offset);
            let plane1 = self.vram.loadb(pattern_offset + 8);
            let bit0 = (plane0 >> (7 - ((x % 8) as u8))) & 1;
            let bit1 = (plane1 >> (7 - ((x % 8) as u8))) & 1;
            let color = (bit1 << 1) | bit0;

            //let r = (tile & 0xc0);
            //let g = (tile & 0x38) << 2;
            //let b = (tile & 0x03) << 5;
            let (r, g, b) = ((color << 6) as u32, (color << 6) as u32, (color << 6) as u32);
            self.putpixel(x, self.scanline as uint, (r << 8) | (g << 16) | (b << 24));
        }
    }

    fn start_vblank(&mut self, result: &mut StepResult) {
        self.regs.status.set_in_vblank(true);

        if self.regs.ctrl.vblank_nmi() {
            debug_print("VBLANK NMI!");
            result.vblank_nmi = true;
        }
    }

    fn step(&mut self, run_to_cycle: u64) -> StepResult {
        let mut result = StepResult { new_frame: false, vblank_nmi: false };
        loop {
            let next_scanline_cycle: u64 = self.cy + CYCLES_PER_SCANLINE;
            if next_scanline_cycle > run_to_cycle {
                break;
            }

            if self.scanline < (SCREEN_HEIGHT as u16) {
                self.render_scanline();
            }

            self.scanline += 1;
            if self.scanline == (VBLANK_SCANLINE as u16) {
                self.start_vblank(&mut result);
            } else if self.scanline == (LAST_SCANLINE as u16) {
                result.new_frame = true;
                self.scanline = 0;
                self.regs.status.set_in_vblank(false);
            }

            self.cy += CYCLES_PER_SCANLINE;

            debug_assert(self.cy % CYCLES_PER_SCANLINE == 0, "at even scanline cycle");
        }

        return result;
    }
}

<|endoftext|>