diff options
Diffstat (limited to 'src/lib/io.rs')
| -rw-r--r-- | src/lib/io.rs | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/src/lib/io.rs b/src/lib/io.rs new file mode 100644 index 00000000..c996584f --- /dev/null +++ b/src/lib/io.rs @@ -0,0 +1,531 @@ +import os::libc; + +native "rust" mod rustrt { + fn rust_get_stdin() -> os::libc::FILE; + fn rust_get_stdout() -> os::libc::FILE; +} + +// Reading + +// FIXME This is all buffered. We might need an unbuffered variant as well + +tag seek_style {seek_set; seek_end; seek_cur;} + +// The raw underlying reader class. All readers must implement this. +type buf_reader = + state obj { + fn read(uint len) -> vec[u8]; + fn read_byte() -> int; + fn unread_byte(int byte); + fn eof() -> bool; + + // FIXME: Seekable really should be orthogonal. We will need + // inheritance. + fn seek(int offset, seek_style whence); + fn tell() -> uint; + }; + +// Convenience methods for reading. +type reader = + state obj { + // FIXME: This should inherit from buf_reader. + fn get_buf_reader() -> buf_reader; + + fn read_byte() -> int; + fn unread_byte(int byte); + fn read_bytes(uint len) -> vec[u8]; + fn read_char() -> char; + fn eof() -> bool; + fn read_line() -> str; + fn read_c_str() -> str; + fn read_le_uint(uint size) -> uint; + fn read_le_int(uint size) -> int; + fn read_be_uint(uint size) -> uint; + fn read_whole_stream() -> vec[u8]; + + fn seek(int offset, seek_style whence); + fn tell() -> uint; // FIXME: eventually u64 + }; + +fn convert_whence(seek_style whence) -> int { + alt (whence) { + case (seek_set) {ret 0;} + case (seek_cur) {ret 1;} + case (seek_end) {ret 2;} + } +} + +state obj FILE_buf_reader(os::libc::FILE f, bool must_close) { + fn read(uint len) -> vec[u8] { + auto buf = _vec::alloc[u8](len); + auto read = os::libc::fread(_vec::buf[u8](buf), 1u, len, f); + _vec::len_set[u8](buf, read); + ret buf; + } + fn read_byte() -> int { + ret os::libc::fgetc(f); + } + fn unread_byte(int byte) { + os::libc::ungetc(byte, f); + } + fn eof() -> bool { + ret os::libc::feof(f) != 0; + } + fn seek(int offset, seek_style whence) { + assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0); + } + fn tell() -> uint { + ret os::libc::ftell(f) as uint; + } + drop { + if (must_close) { os::libc::fclose(f); } + } +} + +// FIXME: Convert this into pseudomethods on buf_reader. +state obj new_reader(buf_reader rdr) { + fn get_buf_reader() -> buf_reader { + ret rdr; + } + fn read_byte() -> int { + ret rdr.read_byte(); + } + fn unread_byte(int byte) { + ret rdr.unread_byte(byte); + } + fn read_bytes(uint len) -> vec[u8] { + ret rdr.read(len); + } + fn read_char() -> char { + auto c0 = rdr.read_byte(); + if (c0 == -1) {ret -1 as char;} // FIXME will this stay valid? + auto b0 = c0 as u8; + auto w = _str::utf8_char_width(b0); + assert (w > 0u); + if (w == 1u) {ret b0 as char;} + auto val = 0u; + while (w > 1u) { + w -= 1u; + auto next = rdr.read_byte(); + assert (next > -1); + assert (next & 0xc0 == 0x80); + val <<= 6u; + val += (next & 0x3f) as uint; + } + // See _str::char_at + val += ((b0 << ((w + 1u) as u8)) as uint) << ((w - 1u) * 6u - w - 1u); + ret val as char; + } + fn eof() -> bool { + ret rdr.eof(); + } + fn read_line() -> str { + let vec[u8] buf = vec(); + // No break yet in rustc + auto go_on = true; + while (go_on) { + auto ch = rdr.read_byte(); + if (ch == -1 || ch == 10) {go_on = false;} + else {_vec::push[u8](buf, ch as u8);} + } + ret _str::unsafe_from_bytes(buf); + } + fn read_c_str() -> str { + let vec[u8] buf = vec(); + auto go_on = true; + while (go_on) { + auto ch = rdr.read_byte(); + if (ch < 1) {go_on = false;} + else {_vec::push[u8](buf, ch as u8);} + } + ret _str::unsafe_from_bytes(buf); + } + // FIXME deal with eof? + fn read_le_uint(uint size) -> uint { + auto val = 0u; + auto pos = 0u; + while (size > 0u) { + val += (rdr.read_byte() as uint) << pos; + pos += 8u; + size -= 1u; + } + ret val; + } + fn read_le_int(uint size) -> int { + auto val = 0u; + auto pos = 0u; + while (size > 0u) { + val += (rdr.read_byte() as uint) << pos; + pos += 8u; + size -= 1u; + } + ret val as int; + } + // FIXME deal with eof? + fn read_be_uint(uint size) -> uint { + auto val = 0u; + auto sz = size; // FIXME: trans::ml bug workaround + while (sz > 0u) { + sz -= 1u; + val += (rdr.read_byte() as uint) << (sz * 8u); + } + ret val; + } + fn read_whole_stream() -> vec[u8] { + let vec[u8] buf = vec(); + while (!rdr.eof()) { + buf += rdr.read(2048u); + } + ret buf; + } + fn seek(int offset, seek_style whence) { + ret rdr.seek(offset, whence); + } + fn tell() -> uint { + ret rdr.tell(); + } +} + +fn stdin() -> reader { + ret new_reader(FILE_buf_reader(rustrt::rust_get_stdin(), false)); +} + +fn file_reader(str path) -> reader { + auto f = os::libc::fopen(_str::buf(path), _str::buf("r")); + if (f as uint == 0u) { + log_err "error opening " + path; + fail; + } + ret new_reader(FILE_buf_reader(f, true)); +} + +// FIXME: Remove me once objects are exported. +fn new_reader_(buf_reader bufr) -> reader { + ret new_reader(bufr); +} + + +// Byte buffer readers + +// TODO: mutable? u8, but this fails with rustboot. +type byte_buf = @rec(vec[u8] buf, mutable uint pos); + +state obj byte_buf_reader(byte_buf bbuf) { + fn read(uint len) -> vec[u8] { + auto rest = _vec::len[u8](bbuf.buf) - bbuf.pos; + auto to_read = len; + if (rest < to_read) { + to_read = rest; + } + auto range = _vec::slice[u8](bbuf.buf, bbuf.pos, bbuf.pos + to_read); + bbuf.pos += to_read; + ret range; + } + fn read_byte() -> int { + if (bbuf.pos == _vec::len[u8](bbuf.buf)) {ret -1;} + auto b = bbuf.buf.(bbuf.pos); + bbuf.pos += 1u; + ret b as int; + } + + fn unread_byte(int byte) { + log_err "TODO: unread_byte"; + fail; + } + + fn eof() -> bool { + ret bbuf.pos == _vec::len[u8](bbuf.buf); + } + + fn seek(int offset, seek_style whence) { + auto pos = bbuf.pos; + auto len = _vec::len[u8](bbuf.buf); + bbuf.pos = seek_in_buf(offset, pos, len, whence); + } + + fn tell() -> uint { ret bbuf.pos; } +} + +fn new_byte_buf_reader(vec[u8] buf) -> byte_buf_reader { + ret byte_buf_reader(@rec(buf=buf, mutable pos=0u)); +} + + +// Writing + +tag fileflag { + append; + create; + truncate; + none; +} + +type buf_writer = state obj { + fn write(vec[u8] v); + + // FIXME: Seekable really should be orthogonal. We will need inheritance. + fn seek(int offset, seek_style whence); + fn tell() -> uint; // FIXME: eventually u64 +}; + +state obj FILE_writer(os::libc::FILE f, bool must_close) { + fn write(vec[u8] v) { + auto len = _vec::len[u8](v); + auto vbuf = _vec::buf[u8](v); + auto nout = os::libc::fwrite(vbuf, len, 1u, f); + if (nout < 1u) { + log_err "error dumping buffer"; + } + } + + fn seek(int offset, seek_style whence) { + assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0); + } + + fn tell() -> uint { + ret os::libc::ftell(f) as uint; + } + + drop { + if (must_close) {os::libc::fclose(f);} + } +} + +state obj fd_buf_writer(int fd, bool must_close) { + fn write(vec[u8] v) { + auto len = _vec::len[u8](v); + auto count = 0u; + auto vbuf; + while (count < len) { + vbuf = _vec::buf_off[u8](v, count); + auto nout = os::libc::write(fd, vbuf, len); + if (nout < 0) { + log_err "error dumping buffer"; + log_err sys::rustrt::last_os_error(); + fail; + } + count += nout as uint; + } + } + + fn seek(int offset, seek_style whence) { + log_err "need 64-bit native calls for seek, sorry"; + fail; + } + + fn tell() -> uint { + log_err "need 64-bit native calls for tell, sorry"; + fail; + } + + drop { + if (must_close) {os::libc::close(fd);} + } +} + +fn file_buf_writer(str path, vec[fileflag] flags) -> buf_writer { + let int fflags = + os::libc_constants::O_WRONLY() | + os::libc_constants::O_BINARY(); + + for (fileflag f in flags) { + alt (f) { + case (append) { fflags |= os::libc_constants::O_APPEND(); } + case (create) { fflags |= os::libc_constants::O_CREAT(); } + case (truncate) { fflags |= os::libc_constants::O_TRUNC(); } + case (none) {} + } + } + + auto fd = os::libc::open(_str::buf(path), + fflags, + os::libc_constants::S_IRUSR() | + os::libc_constants::S_IWUSR()); + + if (fd < 0) { + log_err "error opening file for writing"; + log_err sys::rustrt::last_os_error(); + fail; + } + ret fd_buf_writer(fd, true); +} + +type writer = + state obj { + fn get_buf_writer() -> buf_writer; + // write_str will continue to do utf-8 output only. an alternative + // function will be provided for general encoded string output + fn write_str(str s); + fn write_char(char ch); + fn write_int(int n); + fn write_uint(uint n); + fn write_bytes(vec[u8] bytes); + fn write_le_uint(uint n, uint size); + fn write_le_int(int n, uint size); + fn write_be_uint(uint n, uint size); + }; + +fn uint_to_le_bytes(uint n, uint size) -> vec[u8] { + let vec[u8] bytes = vec(); + while (size > 0u) { + bytes += vec((n & 255u) as u8); + n >>= 8u; + size -= 1u; + } + ret bytes; +} + +fn uint_to_be_bytes(uint n, uint size) -> vec[u8] { + let vec[u8] bytes = vec(); + auto i = (size - 1u) as int; + while (i >= 0) { + bytes += vec(((n >> ((i * 8) as uint)) & 255u) as u8); + i -= 1; + } + ret bytes; +} + +state obj new_writer(buf_writer out) { + fn get_buf_writer() -> buf_writer { + ret out; + } + fn write_str(str s) { + out.write(_str::bytes(s)); + } + fn write_char(char ch) { + // FIXME needlessly consy + out.write(_str::bytes(_str::from_char(ch))); + } + fn write_int(int n) { + out.write(_str::bytes(_int::to_str(n, 10u))); + } + fn write_uint(uint n) { + out.write(_str::bytes(_uint::to_str(n, 10u))); + } + fn write_bytes(vec[u8] bytes) { + out.write(bytes); + } + fn write_le_uint(uint n, uint size) { + out.write(uint_to_le_bytes(n, size)); + } + fn write_le_int(int n, uint size) { + out.write(uint_to_le_bytes(n as uint, size)); + } + fn write_be_uint(uint n, uint size) { + out.write(uint_to_be_bytes(n, size)); + } +} + +// FIXME: Remove me once objects are exported. +fn new_writer_(buf_writer out) -> writer { + ret new_writer(out); +} + +fn file_writer(str path, vec[fileflag] flags) -> writer { + ret new_writer(file_buf_writer(path, flags)); +} + +// FIXME: fileflags +fn buffered_file_buf_writer(str path) -> buf_writer { + auto f = os::libc::fopen(_str::buf(path), _str::buf("w")); + if (f as uint == 0u) { + log_err "error opening " + path; + fail; + } + ret FILE_writer(f, true); +} + +// FIXME it would be great if this could be a const +fn stdout() -> writer { + ret new_writer(fd_buf_writer(1, false)); +} + +type str_writer = + state obj { + fn get_writer() -> writer; + fn get_str() -> str; + }; + +type mutable_byte_buf = @rec(mutable vec[mutable u8] buf, mutable uint pos); + +state obj byte_buf_writer(mutable_byte_buf buf) { + fn write(vec[u8] v) { + // Fast path. + if (buf.pos == _vec::len(buf.buf)) { + // FIXME: Fix our type system. There's no reason you shouldn't be + // able to add a mutable vector to an immutable one. + auto mv = _vec::rustrt::unsafe_vec_to_mut[u8](v); + buf.buf += mv; + buf.pos += _vec::len[u8](v); + ret; + } + + // FIXME: Optimize: These should be unique pointers. + auto vlen = _vec::len[u8](v); + auto vpos = 0u; + while (vpos < vlen) { + auto b = v.(vpos); + if (buf.pos == _vec::len(buf.buf)) { + buf.buf += vec(mutable b); + } else { + buf.buf.(buf.pos) = b; + } + buf.pos += 1u; + vpos += 1u; + } + } + + fn seek(int offset, seek_style whence) { + auto pos = buf.pos; + auto len = _vec::len(buf.buf); + buf.pos = seek_in_buf(offset, pos, len, whence); + } + + fn tell() -> uint { ret buf.pos; } +} + +fn string_writer() -> str_writer { + // FIXME: yikes, this is bad. Needs fixing of mutable syntax. + let vec[mutable u8] b = vec(mutable 0u8); + _vec::pop(b); + + let mutable_byte_buf buf = @rec(mutable buf = b, mutable pos = 0u); + state obj str_writer_wrap(writer wr, mutable_byte_buf buf) { + fn get_writer() -> writer {ret wr;} + fn get_str() -> str {ret _str::unsafe_from_bytes(buf.buf);} + } + ret str_writer_wrap(new_writer(byte_buf_writer(buf)), buf); +} + + +// Utility functions + +fn seek_in_buf(int offset, uint pos, uint len, seek_style whence) -> uint { + auto bpos = pos as int; + auto blen = len as int; + alt (whence) { + case (seek_set) { bpos = offset; } + case (seek_cur) { bpos += offset; } + case (seek_end) { bpos = blen + offset; } + } + + if (bpos < 0) { + bpos = 0; + } else if (bpos > blen) { + bpos = blen; + } + + ret bpos as uint; +} + + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: +// |