about summary refs log tree commit diff
path: root/src/libcore/rt
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2013-05-09 17:37:31 -0700
committerBrian Anderson <banderson@mozilla.com>2013-05-15 12:19:15 -0700
commitb764d4cb4f0c893caf5a6395db9b1e10a167a28f (patch)
treea027d29184fa2408f86825aa0f35f5b4513dd0b6 /src/libcore/rt
parent013b7760b7bbc43ee56179588f8fe1a81d4567e5 (diff)
core::rt: Begin implementing Reader extension methods
Diffstat (limited to 'src/libcore/rt')
-rw-r--r--src/libcore/rt/io/extensions.rs266
-rw-r--r--src/libcore/rt/io/mock.rs50
-rw-r--r--src/libcore/rt/io/mod.rs14
3 files changed, 325 insertions, 5 deletions
diff --git a/src/libcore/rt/io/extensions.rs b/src/libcore/rt/io/extensions.rs
index bb025b0ccb6..665b8a578e3 100644
--- a/src/libcore/rt/io/extensions.rs
+++ b/src/libcore/rt/io/extensions.rs
@@ -13,26 +13,103 @@
 // XXX: Not sure how this should be structured
 // XXX: Iteration should probably be considered separately
 
+use vec;
+use rt::io::Reader;
+use option::{Option, Some, None};
+use unstable::finally::Finally;
+
 pub trait ReaderUtil {
 
+    /// Reads a single byte. Returns `None` on EOF.
+    ///
+    /// # Failure
+    ///
+    /// Raises the same conditions as the `read` method. Returns
+    /// `None` if the condition is handled.
+    fn read_byte(&mut self) -> Option<u8>;
+
+    /// Reads `len` bytes and appends them to a vector.
+    ///
+    /// May push fewer than the requested number of bytes on error
+    /// or EOF. Returns true on success, false on EOF or error.
+    ///
+    /// # Failure
+    ///
+    /// Raises the same conditions as `read`. Returns `false` if
+    /// the condition is handled.
+    fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) -> bool;
+
     /// Reads `len` bytes and gives you back a new vector
     ///
     /// # Failure
     ///
-    /// Raises the `io_error` condition on error. Returns an empty
-    /// vector if the condition is handled.
+    /// Raises the same conditions as the `read` method. May return
+    /// less than the requested number of bytes on error or EOF.
     fn read_bytes(&mut self, len: uint) -> ~[u8];
 
     /// Reads all remaining bytes from the stream.
     ///
     /// # Failure
     ///
-    /// Raises the `io_error` condition on error. Returns an empty
-    /// vector if the condition is handled.
+    /// Raises the same conditions as the `read` method.
     fn read_to_end(&mut self) -> ~[u8];
 
 }
 
+impl<T: Reader> ReaderUtil for T {
+    fn read_byte(&mut self) -> Option<u8> {
+        let mut buf = [0];
+        match self.read(buf) {
+            Some(nread) if nread == 0 => {
+                debug!("read 0 bytes. trying again");
+                self.read_byte()
+            }
+            Some(nread) => Some(buf[0]),
+            None => None
+        }
+    }
+
+    fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) -> bool {
+        unsafe {
+            let start_len = buf.len();
+            let mut total_read = 0;
+            let mut eof = false;
+
+            vec::reserve_at_least(buf, start_len + len);
+            vec::raw::set_len(buf, start_len + len);
+
+            do (|| {
+                while total_read < len {
+                    let slice = vec::mut_slice(*buf, start_len + total_read, buf.len());
+                    match self.read(slice) {
+                        Some(nread) => {
+                            total_read += nread;
+                        }
+                        None => {
+                            eof = true;
+                            break;
+                        }
+                    }
+                }
+            }).finally {
+                vec::raw::set_len(buf, start_len + total_read);
+            }
+
+            return !eof;
+        }
+    }
+
+    fn read_bytes(&mut self, len: uint) -> ~[u8] {
+        let mut buf = vec::with_capacity(len);
+        self.push_bytes(&mut buf, len);
+        return buf;
+    }
+
+    fn read_to_end(&mut self) -> ~[u8] {
+        fail!()
+    }
+}
+
 pub trait ReaderByteConversions {
     /// Reads `n` little-endian unsigned integer bytes.
     ///
@@ -467,3 +544,184 @@ pub trait WriterByteConversions {
     /// Raises the `io_error` condition on error.
     fn write_i8(&mut self, n: i8);
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use option::{Some, None};
+    use cell::Cell;
+    use rt::io::mem::MemReader;
+    use rt::io::mock::*;
+    use rt::io::{io_error, placeholder_error};
+
+    #[test]
+    fn read_byte() {
+        let mut reader = MemReader::new(~[10]);
+        let byte = reader.read_byte();
+        assert!(byte == Some(10));
+    }
+
+    #[test]
+    fn read_byte_0_bytes() {
+        let mut reader = MockReader::new();
+        let count = Cell(0);
+        reader.read = |buf| {
+            do count.with_mut_ref |count| {
+                if *count == 0 {
+                    *count = 1;
+                    Some(0)
+                } else {
+                    buf[0] = 10;
+                    Some(1)
+                }
+            }
+        };
+        let byte = reader.read_byte();
+        assert!(byte == Some(10));
+    }
+
+    #[test]
+    fn read_byte_eof() {
+        let mut reader = MockReader::new();
+        reader.read = |_| None;
+        let byte = reader.read_byte();
+        assert!(byte == None);
+    }
+
+    #[test]
+    fn read_byte_error() {
+        let mut reader = MockReader::new();
+        reader.read = |_| {
+            io_error::cond.raise(placeholder_error());
+            None
+        };
+        do io_error::cond.trap(|_| {
+        }).in {
+            let byte = reader.read_byte();
+            assert!(byte == None);
+        }
+    }
+
+    #[test]
+    fn read_bytes() {
+        let mut reader = MemReader::new(~[10, 11, 12, 13]);
+        let bytes = reader.read_bytes(4);
+        assert!(bytes == ~[10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn read_bytes_partial() {
+        let mut reader = MockReader::new();
+        let count = Cell(0);
+        reader.read = |buf| {
+            do count.with_mut_ref |count| {
+                if *count == 0 {
+                    *count = 1;
+                    buf[0] = 10;
+                    buf[1] = 11;
+                    Some(2)
+                } else {
+                    buf[0] = 12;
+                    buf[1] = 13;
+                    Some(2)
+                }
+            }
+        };
+        let bytes = reader.read_bytes(4);
+        assert!(bytes == ~[10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn push_bytes() {
+        let mut reader = MemReader::new(~[10, 11, 12, 13]);
+        let mut buf = ~[8, 9];
+        assert!(reader.push_bytes(&mut buf, 4));
+        assert!(buf == ~[8, 9, 10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn push_bytes_partial() {
+        let mut reader = MockReader::new();
+        let count = Cell(0);
+        reader.read = |buf| {
+            do count.with_mut_ref |count| {
+                if *count == 0 {
+                    *count = 1;
+                    buf[0] = 10;
+                    buf[1] = 11;
+                    Some(2)
+                } else {
+                    buf[0] = 12;
+                    buf[1] = 13;
+                    Some(2)
+                }
+            }
+        };
+        let mut buf = ~[8, 9];
+        assert!(reader.push_bytes(&mut buf, 4));
+        assert!(buf == ~[8, 9, 10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn push_bytes_eof() {
+        let mut reader = MemReader::new(~[10, 11]);
+        let mut buf = ~[8, 9];
+        assert!(!reader.push_bytes(&mut buf, 4));
+        assert!(buf == ~[8, 9, 10, 11]);
+    }
+
+    #[test]
+    fn push_bytes_error() {
+        let mut reader = MockReader::new();
+        let count = Cell(0);
+        reader.read = |buf| {
+            do count.with_mut_ref |count| {
+                if *count == 0 {
+                    *count = 1;
+                    buf[0] = 10;
+                    Some(1)
+                } else {
+                    io_error::cond.raise(placeholder_error());
+                    None
+                }
+            }
+        };
+        let mut buf = ~[8, 9];
+        do io_error::cond.trap(|_| { } ).in {
+            assert!(!reader.push_bytes(&mut buf, 4));
+        }
+        assert!(buf == ~[8, 9, 10]);
+    }
+
+    #[test]
+    #[should_fail]
+    #[ignore(cfg(windows))]
+    fn push_bytes_fail_reset_len() {
+        use unstable::finally::Finally;
+
+        // push_bytes unsafely sets the vector length. This is testing that
+        // upon failure the length is reset correctly.
+        let mut reader = MockReader::new();
+        let count = Cell(0);
+        reader.read = |buf| {
+            do count.with_mut_ref |count| {
+                if *count == 0 {
+                    *count = 1;
+                    buf[0] = 10;
+                    Some(1)
+                } else {
+                    io_error::cond.raise(placeholder_error());
+                    None
+                }
+            }
+        };
+        let buf = @mut ~[8, 9];
+        do (|| {
+            reader.push_bytes(&mut *buf, 4);
+        }).finally {
+            // NB: Using rtassert here to trigger abort on failure since this is a should_fail test
+            rtassert!(*buf == ~[8, 9, 10]);
+        }
+    }
+
+}
diff --git a/src/libcore/rt/io/mock.rs b/src/libcore/rt/io/mock.rs
new file mode 100644
index 00000000000..b580b752bd9
--- /dev/null
+++ b/src/libcore/rt/io/mock.rs
@@ -0,0 +1,50 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use option::{Option, None};
+use rt::io::{Reader, Writer};
+
+pub struct MockReader {
+    read: ~fn(buf: &mut [u8]) -> Option<uint>,
+    eof: ~fn() -> bool
+}
+
+impl MockReader {
+    pub fn new() -> MockReader {
+        MockReader {
+            read: |_| None,
+            eof: || false
+        }
+    }
+}
+
+impl Reader for MockReader {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> { (self.read)(buf) }
+    fn eof(&mut self) -> bool { (self.eof)() }
+}
+
+pub struct MockWriter {
+    write: ~fn(buf: &[u8]),
+    flush: ~fn()
+}
+
+impl MockWriter {
+    pub fn new() -> MockWriter {
+        MockWriter {
+            write: |_| (),
+            flush: || ()
+        }
+    }
+}
+
+impl Writer for MockWriter {
+    fn write(&mut self, buf: &[u8]) { (self.write)(buf) }
+    fn flush(&mut self) { (self.flush)() }
+}
\ No newline at end of file
diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs
index ab4b83f7cf0..f3b0cd22c17 100644
--- a/src/libcore/rt/io/mod.rs
+++ b/src/libcore/rt/io/mod.rs
@@ -316,6 +316,8 @@ pub mod native {
     }
 }
 
+/// Mock implementations for testing
+mod mock;
 
 /// The type passed to I/O condition handlers to indicate error
 ///
@@ -350,7 +352,8 @@ condition! {
 
 pub trait Reader {
     /// Read bytes, up to the length of `buf` and place them in `buf`.
-    /// Returns the number of bytes read, or `None` on EOF.
+    /// Returns the number of bytes read, or `None` on EOF. The number
+    /// of bytes read my be less than the number requested, even 0.
     ///
     /// # Failure
     ///
@@ -361,6 +364,7 @@ pub trait Reader {
     /// This doesn't take a `len` argument like the old `read`.
     /// Will people often need to slice their vectors to call this
     /// and will that be annoying?
+    /// Is it actually possible for 0 bytes to be read successfully?
     fn read(&mut self, buf: &mut [u8]) -> Option<uint>;
 
     /// Return whether the Reader has reached the end of the stream.
@@ -467,3 +471,11 @@ pub fn standard_error(kind: IoErrorKind) -> IoError {
         _ => fail!()
     }
 }
+
+pub fn placeholder_error() -> IoError {
+    IoError {
+        kind: OtherIoError,
+        desc: "Placeholder error. You shouldn't be seeing this",
+        detail: None
+    }
+}
\ No newline at end of file