about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorBrian Anderson <andersrb@gmail.com>2012-11-04 12:51:34 -0800
committerBrian Anderson <andersrb@gmail.com>2012-11-04 12:51:34 -0800
commit45a908cad04b10fea8446fbd2589a499f7fa7617 (patch)
tree1f46d0b553e707c0f07791bc341c4239f0b9166b /src/libcore
parentff07e687588d6a449d959c73326aa0147083af62 (diff)
parentfd6f62f9c71722282eb71ae040290e1addab8282 (diff)
Merge pull request #3916 from Dretch/iofix
More methods/docs/tests for core::io
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/io.rs380
1 files changed, 343 insertions, 37 deletions
diff --git a/src/libcore/io.rs b/src/libcore/io.rs
index c0e17230010..4629878b4b7 100644
--- a/src/libcore/io.rs
+++ b/src/libcore/io.rs
@@ -32,38 +32,131 @@ extern mod rustrt {
 pub enum SeekStyle { SeekSet, SeekEnd, SeekCur, }
 
 
-// The raw underlying reader trait. All readers must implement this.
+/// The raw underlying reader trait. All readers must implement this.
 pub trait Reader {
     // FIXME (#2004): Seekable really should be orthogonal.
 
+    /// Read up to len bytes (or EOF) and put them into bytes (which
+    /// must be at least len bytes long). Return number of bytes read.
     // FIXME (#2982): This should probably return an error.
     fn read(bytes: &[mut u8], len: uint) -> uint;
+
+    /// Read a single byte, returning a negative value for EOF or read error.
     fn read_byte() -> int;
+
+    /// Behaves like the libc function ungetc.
     fn unread_byte(int);
+
+    /// Return whether the stream is currently at EOF position.
     fn eof() -> bool;
-    fn seek(int, SeekStyle);
+
+    /// Move the current position within the stream. The second parameter
+    /// determines the position that the first parameter is relative to.
+    fn seek(position: int, style: SeekStyle);
+
+    /// Return the current position within the stream.
     fn tell() -> uint;
 }
 
-// Generic utility functions defined on readers
-
+/// Generic utility functions defined on readers.
 pub trait ReaderUtil {
+
+    /// Read len bytes into a new vec.
     fn read_bytes(len: uint) -> ~[u8];
+
+    /// Read up until the first '\n' char (which is not returned), or EOF.
     fn read_line() -> ~str;
 
+    /// Read n utf-8 encoded chars.
     fn read_chars(n: uint) -> ~[char];
+
+    /// Read a single utf-8 encoded char.
     fn read_char() -> char;
+
+    /// Read up until the first null byte (which is not returned), or EOF.
     fn read_c_str() -> ~str;
-    fn read_le_uint(size: uint) -> uint;
-    fn read_le_int(size: uint) -> int;
-    fn read_be_uint(size: uint) -> uint;
+
+    /// Read all the data remaining in the stream in one go.
     fn read_whole_stream() -> ~[u8];
+
+    /// Iterate over every byte until the iterator breaks or EOF.
     fn each_byte(it: fn(int) -> bool);
+
+    /// Iterate over every char until the iterator breaks or EOF.
     fn each_char(it: fn(char) -> bool);
-    fn each_line(it: fn((&str)) -> bool);
+
+    /// Iterate over every line until the iterator breaks or EOF.
+    fn each_line(it: fn(&str) -> bool);
+
+    /// Read n (between 1 and 8) little-endian unsigned integer bytes.
+    fn read_le_uint_n(nbytes: uint) -> u64;
+
+    /// Read n (between 1 and 8) little-endian signed integer bytes.
+    fn read_le_int_n(nbytes: uint) -> i64;
+
+    /// Read n (between 1 and 8) big-endian unsigned integer bytes.
+    fn read_be_uint_n(nbytes: uint) -> u64;
+
+    /// Read n (between 1 and 8) big-endian signed integer bytes.
+    fn read_be_int_n(nbytes: uint) -> i64;
+
+    /// Read a little-endian uint (number of bytes depends on system).
+    fn read_le_uint() -> uint;
+
+    /// Read a little-endian int (number of bytes depends on system).
+    fn read_le_int() -> int;
+
+    /// Read a big-endian uint (number of bytes depends on system).
+    fn read_be_uint() -> uint;
+
+    /// Read a big-endian int (number of bytes depends on system).
+    fn read_be_int() -> int;
+
+    /// Read a big-endian u64 (8 bytes).
+    fn read_be_u64() -> u64;
+
+    /// Read a big-endian u32 (4 bytes).
+    fn read_be_u32() -> u32;
+
+    /// Read a big-endian u16 (2 bytes).
+    fn read_be_u16() -> u16;
+
+    /// Read a big-endian i64 (8 bytes).
+    fn read_be_i64() -> i64;
+
+    /// Read a big-endian i32 (4 bytes).
+    fn read_be_i32() -> i32;
+
+    /// Read a big-endian i16 (2 bytes).
+    fn read_be_i16() -> i16;
+
+    /// Read a little-endian u64 (8 bytes).
+    fn read_le_u64() -> u64;
+
+    /// Read a little-endian u32 (4 bytes).
+    fn read_le_u32() -> u32;
+
+    /// Read a little-endian u16 (2 bytes).
+    fn read_le_u16() -> u16;
+
+    /// Read a litle-endian i64 (8 bytes).
+    fn read_le_i64() -> i64;
+
+    /// Read a litle-endian i32 (4 bytes).
+    fn read_le_i32() -> i32;
+
+    /// Read a litle-endian i16 (2 bytes).
+    fn read_le_i16() -> i16;
+
+    /// Read a u8 (1 byte).
+    fn read_u8() -> u8;
+
+    /// Read a i8 (1 byte).
+    fn read_i8() -> i8;
 }
 
 impl<T: Reader> T : ReaderUtil {
+
     fn read_bytes(len: uint) -> ~[u8] {
         let mut bytes = vec::with_capacity(len);
         unsafe { vec::raw::set_len(&mut bytes, len); }
@@ -73,6 +166,7 @@ impl<T: Reader> T : ReaderUtil {
         unsafe { vec::raw::set_len(&mut bytes, count); }
         move bytes
     }
+
     fn read_line() -> ~str {
         let mut bytes = ~[];
         loop {
@@ -162,34 +256,6 @@ impl<T: Reader> T : ReaderUtil {
         str::from_bytes(bytes)
     }
 
-    // FIXME deal with eof? // #2004
-    fn read_le_uint(size: uint) -> uint {
-        let mut val = 0u, pos = 0u, i = size;
-        while i > 0u {
-            val += (self.read_byte() as uint) << pos;
-            pos += 8u;
-            i -= 1u;
-        }
-        val
-    }
-    fn read_le_int(size: uint) -> int {
-        let mut val = 0u, pos = 0u, i = size;
-        while i > 0u {
-            val += (self.read_byte() as uint) << pos;
-            pos += 8u;
-            i -= 1u;
-        }
-        val as int
-    }
-    fn read_be_uint(size: uint) -> uint {
-        let mut val = 0u, i = size;
-        while i > 0u {
-            i -= 1u;
-            val += (self.read_byte() as uint) << i * 8u;
-        }
-        val
-    }
-
     fn read_whole_stream() -> ~[u8] {
         let mut bytes: ~[u8] = ~[];
         while !self.eof() { bytes.push_all(self.read_bytes(2048u)); }
@@ -213,6 +279,116 @@ impl<T: Reader> T : ReaderUtil {
             if !it(self.read_line()) { break; }
         }
     }
+
+    // FIXME int reading methods need to deal with eof - issue #2004
+
+    fn read_le_uint_n(nbytes: uint) -> u64 {
+        assert nbytes > 0 && nbytes <= 8;
+
+        let mut val = 0u64, pos = 0, i = nbytes;
+        while i > 0 {
+            val += (self.read_u8() as u64) << pos;
+            pos += 8;
+            i -= 1;
+        }
+        val
+    }
+
+    fn read_le_int_n(nbytes: uint) -> i64 {
+        extend_sign(self.read_le_uint_n(nbytes), nbytes)
+    }
+
+    fn read_be_uint_n(nbytes: uint) -> u64 {
+        assert nbytes > 0 && nbytes <= 8;
+
+        let mut val = 0u64, i = nbytes;
+        while i > 0 {
+            i -= 1;
+            val += (self.read_u8() as u64) << i * 8;
+        }
+        val
+    }
+
+    fn read_be_int_n(nbytes: uint) -> i64 {
+        extend_sign(self.read_be_uint_n(nbytes), nbytes)
+    }
+
+    fn read_le_uint() -> uint {
+        self.read_le_uint_n(uint::bytes) as uint
+    }
+
+    fn read_le_int() -> int {
+        self.read_le_int_n(int::bytes) as int
+    }
+
+    fn read_be_uint() -> uint {
+        self.read_be_uint_n(uint::bytes) as uint
+    }
+
+    fn read_be_int() -> int {
+        self.read_be_int_n(int::bytes) as int
+    }
+
+    fn read_be_u64() -> u64 {
+        self.read_be_uint_n(8) as u64
+    }
+
+    fn read_be_u32() -> u32 {
+        self.read_be_uint_n(4) as u32
+    }
+
+    fn read_be_u16() -> u16 {
+        self.read_be_uint_n(2) as u16
+    }
+
+    fn read_be_i64() -> i64 {
+        self.read_be_int_n(8) as i64
+    }
+
+    fn read_be_i32() -> i32 {
+        self.read_be_int_n(4) as i32
+    }
+
+    fn read_be_i16() -> i16 {
+        self.read_be_int_n(2) as i16
+    }
+
+    fn read_le_u64() -> u64 {
+        self.read_le_uint_n(8) as u64
+    }
+
+    fn read_le_u32() -> u32 {
+        self.read_le_uint_n(4) as u32
+    }
+
+    fn read_le_u16() -> u16 {
+        self.read_le_uint_n(2) as u16
+    }
+
+    fn read_le_i64() -> i64 {
+        self.read_le_int_n(8) as i64
+    }
+
+    fn read_le_i32() -> i32 {
+        self.read_le_int_n(4) as i32
+    }
+
+    fn read_le_i16() -> i16 {
+        self.read_le_int_n(2) as i16
+    }
+
+    fn read_u8() -> u8 {
+        self.read_byte() as u8
+    }
+
+    fn read_i8() -> i8 {
+        self.read_byte() as i8
+    }
+}
+
+fn extend_sign(val: u64, nbytes: uint) -> i64 {
+    let shift = (8 - nbytes) * 8;
+    (val << shift) as i64 >> shift
 }
 
 // Reader implementations
@@ -358,11 +534,23 @@ pub impl WriterType : Eq {
 
 // FIXME (#2004): Seekable really should be orthogonal.
 // FIXME (#2004): eventually u64
+/// The raw underlying writer trait. All writers must implement this.
 pub trait Writer {
+
+    /// Write all of the given bytes.
     fn write(v: &[const u8]);
+
+    /// Move the current position within the stream. The second parameter
+    /// determines the position that the first parameter is relative to.
     fn seek(int, SeekStyle);
+
+    /// Return the current position within the stream.
     fn tell() -> uint;
+
+    /// Flush the output buffer for this stream (if there is one).
     fn flush() -> int;
+
+    /// Determine if this Writer is writing to a file or not.
     fn get_type() -> WriterType;
 }
 
@@ -566,29 +754,77 @@ pub fn u64_from_be_bytes(data: &[const u8],
 
 // FIXME: #3048 combine trait+impl (or just move these to
 // default methods on writer)
+/// Generic utility functions defined on writers.
 pub trait WriterUtil {
+
+    /// Write a single utf-8 encoded char.
     fn write_char(ch: char);
+
+    /// Write every char in the given str, encoded as utf-8.
     fn write_str(s: &str);
+
+    /// Write the given str, as utf-8, followed by '\n'.
     fn write_line(s: &str);
+
+    /// Write the result of passing n through `int::to_str_bytes`.
     fn write_int(n: int);
+
+    /// Write the result of passing n through `uint::to_str_bytes`.
     fn write_uint(n: uint);
+
+    /// Write a little-endian uint (number of bytes depends on system).
     fn write_le_uint(n: uint);
+
+    /// Write a little-endian int (number of bytes depends on system).
     fn write_le_int(n: int);
+
+    /// Write a big-endian uint (number of bytes depends on system).
     fn write_be_uint(n: uint);
+
+    /// Write a big-endian int (number of bytes depends on system).
     fn write_be_int(n: int);
+
+    /// Write a big-endian u64 (8 bytes).
     fn write_be_u64(n: u64);
+
+    /// Write a big-endian u32 (4 bytes).
     fn write_be_u32(n: u32);
+
+    /// Write a big-endian u16 (2 bytes).
     fn write_be_u16(n: u16);
+
+    /// Write a big-endian i64 (8 bytes).
     fn write_be_i64(n: i64);
+
+    /// Write a big-endian i32 (4 bytes).
     fn write_be_i32(n: i32);
+
+    /// Write a big-endian i16 (2 bytes).
     fn write_be_i16(n: i16);
+
+    /// Write a little-endian u64 (8 bytes).
     fn write_le_u64(n: u64);
+
+    /// Write a little-endian u32 (4 bytes).
     fn write_le_u32(n: u32);
+
+    /// Write a little-endian u16 (2 bytes).
     fn write_le_u16(n: u16);
+
+    /// Write a little-endian i64 (8 bytes).
     fn write_le_i64(n: i64);
+
+    /// Write a little-endian i32 (4 bytes).
     fn write_le_i32(n: i32);
+
+    /// Write a little-endian i16 (2 bytes).
     fn write_le_i16(n: i16);
+
+    /// Write a u8 (1 byte).
     fn write_u8(n: u8);
+
+    /// Write a i8 (1 byte).
+    fn write_i8(n: i8);
 }
 
 impl<T: Writer> T : WriterUtil {
@@ -659,7 +895,8 @@ impl<T: Writer> T : WriterUtil {
         u64_to_le_bytes(n as u64, 2u, |v| self.write(v))
     }
 
-    fn write_u8(n: u8) { self.write(&[n]) }
+    fn write_u8(n: u8) { self.write([n]) }
+    fn write_i8(n: i8) { self.write([n as u8]) }
 }
 
 #[allow(non_implicitly_copyable_typarams)]
@@ -1001,6 +1238,75 @@ mod tests {
         assert wr.bytes.borrow(|bytes| bytes ==
             ~[0u8, 9u8, 4u8, 5u8, 8u8, 7u8]);
     }
+
+    #[test]
+    fn test_read_write_le() {
+        let path = Path("tmp/lib-io-test-read-write-le.tmp");
+        let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value];
+
+        // write the ints to the file
+        {
+            let file = io::file_writer(&path, [io::Create]).get();
+            for uints.each |i| {
+                file.write_le_u64(*i);
+            }
+        }
+
+        // then read them back and check that they are the same
+        {
+            let file = io::file_reader(&path).get();
+            for uints.each |i| {
+                assert file.read_le_u64() == *i;
+            }
+        }
+    }
+
+    #[test]
+    fn test_read_write_be() {
+        let path = Path("tmp/lib-io-test-read-write-be.tmp");
+        let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value];
+
+        // write the ints to the file
+        {
+            let file = io::file_writer(&path, [io::Create]).get();
+            for uints.each |i| {
+                file.write_be_u64(*i);
+            }
+        }
+
+        // then read them back and check that they are the same
+        {
+            let file = io::file_reader(&path).get();
+            for uints.each |i| {
+                assert file.read_be_u64() == *i;
+            }
+        }
+    }
+
+    #[test]
+    fn test_read_be_int_n() {
+        let path = Path("tmp/lib-io-test-read-be-int-n.tmp");
+        let ints = [i32::min_value, -123456, -42, -5, 0, 1, i32::max_value];
+
+        // write the ints to the file
+        {
+            let file = io::file_writer(&path, [io::Create]).get();
+            for ints.each |i| {
+                file.write_be_i32(*i);
+            }
+        }
+
+        // then read them back and check that they are the same
+        {
+            let file = io::file_reader(&path).get();
+            for ints.each |i| {
+                // this tests that the sign extension is working
+                // (comparing the values as i32 would not test this)
+                assert file.read_be_int_n(4) == *i as i64;
+            }
+        }
+    }
+
 }
 
 //