diff options
| author | Brian Anderson <banderson@mozilla.com> | 2013-05-09 17:37:31 -0700 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2013-05-15 12:19:15 -0700 |
| commit | b764d4cb4f0c893caf5a6395db9b1e10a167a28f (patch) | |
| tree | a027d29184fa2408f86825aa0f35f5b4513dd0b6 /src/libcore/rt | |
| parent | 013b7760b7bbc43ee56179588f8fe1a81d4567e5 (diff) | |
core::rt: Begin implementing Reader extension methods
Diffstat (limited to 'src/libcore/rt')
| -rw-r--r-- | src/libcore/rt/io/extensions.rs | 266 | ||||
| -rw-r--r-- | src/libcore/rt/io/mock.rs | 50 | ||||
| -rw-r--r-- | src/libcore/rt/io/mod.rs | 14 |
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 |
