about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorJeff Olson <olson.jeffery@gmail.com>2013-08-20 15:38:41 -0700
committerJeff Olson <olson.jeffery@gmail.com>2013-08-22 16:31:57 -0700
commit05c8cc70c90032d578606e811bbb1266cd8d2fcd (patch)
tree26d35271abb9c78c159499477956fe2aba0d60c9 /src/libstd
parent48d67610285b423e806e0c44be43d01d5c1a7dbc (diff)
downloadrust-05c8cc70c90032d578606e811bbb1266cd8d2fcd.tar.gz
rust-05c8cc70c90032d578606e811bbb1266cd8d2fcd.zip
std: rework file io.. support [p]read,[p]write, impl seek/tell + more tests
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/rt/io/file.rs140
-rw-r--r--src/libstd/rt/io/mod.rs1
-rw-r--r--src/libstd/rt/rtio.rs15
-rw-r--r--src/libstd/rt/uv/uvio.rs111
4 files changed, 214 insertions, 53 deletions
diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs
index 6973e117131..edc2953a4dd 100644
--- a/src/libstd/rt/io/file.rs
+++ b/src/libstd/rt/io/file.rs
@@ -11,16 +11,15 @@
 use prelude::*;
 use super::support::PathLike;
 use super::{Reader, Writer, Seek};
-use super::SeekStyle;
-use rt::rtio::{RtioFileDescriptor, IoFactory, IoFactoryObject};
+use super::{SeekSet, SeekStyle};
+use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject};
 use rt::io::{io_error, read_error, EndOfFile};
 use rt::local::Local;
 use rt::test::*;
 use libc::{O_RDWR, O_RDONLY, O_WRONLY, S_IWUSR, S_IRUSR,
            O_CREAT, O_TRUNC, O_APPEND};
 
-/// # FIXME #7785
-/// * Ugh, this is ridiculous. What is the best way to represent these options?
+/// Instructions on how to open a file and return a `FileStream`.
 enum FileMode {
     /// Opens an existing file. IoError if file does not exist.
     Open,
@@ -36,15 +35,29 @@ enum FileMode {
     CreateOrTruncate,
 }
 
+/// How should the file be opened? `FileStream`s opened with `Read` will
+/// raise an `io_error` condition if written to.
 enum FileAccess {
     Read,
     Write,
     ReadWrite
 }
 
+/// Abstraction representing *positional* access to a file. In this case,
+/// *positional* refers to it keeping an encounter *cursor* of where in the
+/// file a subsequent `read` or `write` will begin from. Users of a `FileStream`
+/// can `seek` to move the cursor to a given location *within the bounds of the
+/// file* and can ask to have the `FileStream` `tell` them the location, in
+/// bytes, of the cursor.
+///
+/// This abstraction is roughly modeled on the access workflow as represented
+/// by `open(2)`, `read(2)`, `write(2)` and friends.
+///
+/// The `open` and `unlink` static methods are provided to manage creation/removal
+/// of files. All other methods operatin on an instance of `FileStream`.
 pub struct FileStream {
-    fd: ~RtioFileDescriptor,
-    last_nread: int
+    fd: ~RtioFileStream,
+    last_nread: int,
 }
 
 impl FileStream {
@@ -101,7 +114,7 @@ impl FileStream {
 
 impl Reader for FileStream {
     fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
-        match self.fd.read(buf, 0) {
+        match self.fd.read(buf) {
             Ok(read) => {
                 self.last_nread = read;
                 match read {
@@ -126,7 +139,7 @@ impl Reader for FileStream {
 
 impl Writer for FileStream {
     fn write(&mut self, buf: &[u8]) {
-        match self.fd.write(buf, 0) {
+        match self.fd.write(buf) {
             Ok(_) => (),
             Err(ioerr) => {
                 io_error::cond.raise(ioerr);
@@ -134,13 +147,46 @@ impl Writer for FileStream {
         }
     }
 
-    fn flush(&mut self) { fail!() }
+    fn flush(&mut self) {
+        match self.fd.flush() {
+            Ok(_) => (),
+            Err(ioerr) => {
+                read_error::cond.raise(ioerr);
+            }
+        }
+    }
 }
 
 impl Seek for FileStream {
-    fn tell(&self) -> u64 { fail!() }
+    fn tell(&self) -> u64 {
+        let res = self.fd.tell();
+        match res {
+            Ok(cursor) => cursor,
+            Err(ioerr) => {
+                read_error::cond.raise(ioerr);
+                return -1;
+            }
+        }
+    }
 
-    fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() }
+    fn seek(&mut self, pos: i64, style: SeekStyle) {
+        use libc::{SEEK_SET, SEEK_CUR, SEEK_END};
+        let whence = match style {
+            SeekSet => SEEK_SET,
+            SeekCur => SEEK_CUR,
+            SeekEnd => SEEK_END
+        } as i64;
+        match self.fd.seek(pos, whence) {
+            Ok(_) => {
+                // successful seek resets EOF indocator
+                self.last_nread = -1;
+                ()
+            },
+            Err(ioerr) => {
+                read_error::cond.raise(ioerr);
+            }
+        }
+    }
 }
 
 fn file_test_smoke_test_impl() {
@@ -166,7 +212,7 @@ fn file_test_smoke_test_impl() {
 }
 
 #[test]
-fn file_test_smoke_test() {
+fn file_test_io_smoke_test() {
     file_test_smoke_test_impl();
 }
 
@@ -184,17 +230,15 @@ fn file_test_invalid_path_opened_without_create_should_raise_condition_impl() {
     }
 }
 #[test]
-fn file_test_invalid_path_opened_without_create_should_raise_condition() {
+fn file_test_io_invalid_path_opened_without_create_should_raise_condition() {
     file_test_invalid_path_opened_without_create_should_raise_condition_impl();
 }
 
 fn file_test_unlinking_invalid_path_should_raise_condition_impl() {
-    use io;
     do run_in_newsched_task {
         let filename = &Path("./another_file_that_does_not_exist.txt");
         let mut called = false;
         do io_error::cond.trap(|e| {
-            io::println(fmt!("condition kind: %?", e.kind));
             called = true;
         }).inside {
             FileStream::unlink(filename);
@@ -203,6 +247,70 @@ fn file_test_unlinking_invalid_path_should_raise_condition_impl() {
     }
 }
 #[test]
-fn file_test_unlinking_invalid_path_should_raise_condition() {
+fn file_test_iounlinking_invalid_path_should_raise_condition() {
     file_test_unlinking_invalid_path_should_raise_condition_impl();
 }
+
+fn file_test_io_non_positional_read_impl() {
+    do run_in_newsched_task {
+        use str;
+        let message = "ten-four";
+        let mut read_mem = [0, .. 8];
+        let filename = &Path("./rt_io_file_test_positional.txt");
+        {
+            let mut rw_stream = FileStream::open(filename, Create, ReadWrite).unwrap();
+            rw_stream.write(message.as_bytes());
+        }
+        {
+            let mut read_stream = FileStream::open(filename, Open, Read).unwrap();
+            {
+                let read_buf = read_mem.mut_slice(0, 4);
+                read_stream.read(read_buf);
+            }
+            {
+                let read_buf = read_mem.mut_slice(4, 8);
+                read_stream.read(read_buf);
+            }
+        }
+        FileStream::unlink(filename);
+        let read_str = str::from_bytes(read_mem);
+        assert!(read_str == message.to_owned());
+    }
+}
+
+#[test]
+fn file_test_io_non_positional_read() {
+    file_test_io_non_positional_read_impl();
+}
+
+fn file_test_io_seeking_impl() {
+    do run_in_newsched_task {
+        use str;
+        let message = "ten-four";
+        let mut read_mem = [0, .. 4];
+        let set_cursor = 4 as u64;
+        let mut tell_pos_pre_read;
+        let mut tell_pos_post_read;
+        let filename = &Path("./rt_io_file_test_seeking.txt");
+        {
+            let mut rw_stream = FileStream::open(filename, Create, ReadWrite).unwrap();
+            rw_stream.write(message.as_bytes());
+        }
+        {
+            let mut read_stream = FileStream::open(filename, Open, Read).unwrap();
+            read_stream.seek(set_cursor as i64, SeekSet);
+            tell_pos_pre_read = read_stream.tell();
+            read_stream.read(read_mem);
+            tell_pos_post_read = read_stream.tell();
+        }
+        FileStream::unlink(filename);
+        let read_str = str::from_bytes(read_mem);
+        assert!(read_str == message.slice(4, 8).to_owned());
+        assert!(tell_pos_pre_read == set_cursor);
+        assert!(tell_pos_post_read == message.len() as u64);
+    }
+}
+#[test]
+fn file_test_io_seek_and_tell_smoke_test() {
+    file_test_io_seeking_impl();
+}
diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs
index 8b18fbcfa45..23b8017bc51 100644
--- a/src/libstd/rt/io/mod.rs
+++ b/src/libstd/rt/io/mod.rs
@@ -461,6 +461,7 @@ pub enum SeekStyle {
 /// # XXX
 /// * Are `u64` and `i64` the right choices?
 pub trait Seek {
+    /// Return position of file cursor in the stream
     fn tell(&self) -> u64;
 
     /// Seek to an offset in a stream
diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs
index fedffa32844..3a14f679fa7 100644
--- a/src/libstd/rt/rtio.rs
+++ b/src/libstd/rt/rtio.rs
@@ -66,9 +66,9 @@ pub trait IoFactory {
     fn tcp_bind(&mut self, addr: SocketAddr) -> Result<~RtioTcpListenerObject, IoError>;
     fn udp_bind(&mut self, addr: SocketAddr) -> Result<~RtioUdpSocketObject, IoError>;
     fn timer_init(&mut self) -> Result<~RtioTimerObject, IoError>;
-    fn fs_from_raw_fd(&mut self, fd: c_int, close_on_drop: bool) -> ~RtioFileDescriptor;
+    fn fs_from_raw_fd(&mut self, fd: c_int, close_on_drop: bool) -> ~RtioFileStream;
     fn fs_open<P: PathLike>(&mut self, path: &P, flags: int, mode:int)
-        -> Result<~RtioFileDescriptor, IoError>;
+        -> Result<~RtioFileStream, IoError>;
     fn fs_unlink<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>;
 }
 
@@ -113,7 +113,12 @@ pub trait RtioTimer {
     fn sleep(&mut self, msecs: u64);
 }
 
-pub trait RtioFileDescriptor {
-    fn read(&mut self, buf: &mut [u8], offset: i64) -> Result<int, IoError>;
-    fn write(&mut self, buf: &[u8], offset: i64) -> Result<(), IoError>;
+pub trait RtioFileStream {
+    fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError>;
+    fn write(&mut self, buf: &[u8]) -> Result<(), IoError>;
+    fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError>;
+    fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError>;
+    fn seek(&mut self, pos: i64, whence: i64) -> Result<(), IoError>;
+    fn tell(&self) -> Result<u64, IoError>;
+    fn flush(&mut self) -> Result<(), IoError>;
 }
diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs
index 2acf2cccfea..2a62c782d0b 100644
--- a/src/libstd/rt/uv/uvio.rs
+++ b/src/libstd/rt/uv/uvio.rs
@@ -31,6 +31,7 @@ use rt::uv::idle::IdleWatcher;
 use rt::uv::net::{UvIpv4SocketAddr, UvIpv6SocketAddr};
 use unstable::sync::Exclusive;
 use super::super::io::support::PathLike;
+use libc::{lseek, c_long, SEEK_CUR};
 
 #[cfg(test)] use container::Container;
 #[cfg(test)] use unstable::run_in_bare_thread;
@@ -458,19 +459,20 @@ impl IoFactory for UvIoFactory {
         Ok(~UvTimer::new(watcher, home))
     }
 
-    fn fs_from_raw_fd(&mut self, fd: c_int, close_on_drop: bool) -> ~RtioFileDescriptor {
-        ~UvFileDescriptor {
+    fn fs_from_raw_fd(&mut self, fd: c_int, close_on_drop: bool) -> ~RtioFileStream {
+        ~UvFileStream {
             loop_: Loop{handle:self.uv_loop().native_handle()},
             fd: file::FileDescriptor(fd),
-            close_on_drop: close_on_drop
-        } as ~RtioFileDescriptor
+            close_on_drop: close_on_drop,
+        } as ~RtioFileStream
     }
 
     fn fs_open<P: PathLike>(&mut self, path: &P, flags: int, mode: int)
-        -> Result<~RtioFileDescriptor, IoError> {
+        -> Result<~RtioFileStream, IoError> {
         let loop_ = Loop {handle: self.uv_loop().native_handle()};
         let result_cell = Cell::new_empty();
-        let result_cell_ptr: *Cell<Result<~RtioFileDescriptor, IoError>> = &result_cell;
+        let result_cell_ptr: *Cell<Result<~RtioFileStream,
+                                           IoError>> = &result_cell;
         let path_cell = Cell::new(path);
         let scheduler = Local::take::<Scheduler>();
         do scheduler.deschedule_running_task_and_then |_, task| {
@@ -478,10 +480,10 @@ impl IoFactory for UvIoFactory {
             let path = path_cell.take();
             do file::FileDescriptor::open(loop_, path, flags, mode) |req,err| {
                 if err.is_none() {
-                    let res = Ok(~UvFileDescriptor {
+                    let res = Ok(~UvFileStream {
                         loop_: loop_,
                         fd: file::FileDescriptor(req.get_result()),
-                        close_on_drop: true} as ~RtioFileDescriptor);
+                        close_on_drop: true} as ~RtioFileStream);
                     unsafe { (*result_cell_ptr).put_back(res); }
                     let scheduler = Local::take::<Scheduler>();
                     scheduler.resume_blocked_task_immediately(task_cell.take());
@@ -1056,32 +1058,14 @@ impl RtioTimer for UvTimer {
     }
 }
 
-pub struct UvFileDescriptor {
+pub struct UvFileStream {
     loop_: Loop,
     fd: file::FileDescriptor,
     close_on_drop: bool
 }
 
-impl UvFileDescriptor {
-}
-
-impl Drop for UvFileDescriptor {
-    fn drop(&self) {
-        if self.close_on_drop {
-            let scheduler = Local::take::<Scheduler>();
-            do scheduler.deschedule_running_task_and_then |_, task| {
-                let task_cell = Cell::new(task);
-                do self.fd.close(self.loop_) |_,_| {
-                    let scheduler = Local::take::<Scheduler>();
-                    scheduler.resume_blocked_task_immediately(task_cell.take());
-                };
-            };
-        }
-    }
-}
-
-impl RtioFileDescriptor for UvFileDescriptor {
-    fn read(&mut self, buf: &mut [u8], offset: i64) -> Result<int, IoError> {
+impl UvFileStream {
+    fn base_read(&mut self, buf: &mut [u8], offset: i64) -> Result<int, IoError> {
         let scheduler = Local::take::<Scheduler>();
         let result_cell = Cell::new_empty();
         let result_cell_ptr: *Cell<Result<int, IoError>> = &result_cell;
@@ -1101,7 +1085,7 @@ impl RtioFileDescriptor for UvFileDescriptor {
         };
         result_cell.take()
     }
-    fn write(&mut self, buf: &[u8], offset: i64) -> Result<(), IoError> {
+    fn base_write(&mut self, buf: &[u8], offset: i64) -> Result<(), IoError> {
         let scheduler = Local::take::<Scheduler>();
         let result_cell = Cell::new_empty();
         let result_cell_ptr: *Cell<Result<(), IoError>> = &result_cell;
@@ -1123,6 +1107,69 @@ impl RtioFileDescriptor for UvFileDescriptor {
     }
 }
 
+impl Drop for UvFileStream {
+    fn drop(&self) {
+        if self.close_on_drop {
+            let scheduler = Local::take::<Scheduler>();
+            do scheduler.deschedule_running_task_and_then |_, task| {
+                let task_cell = Cell::new(task);
+                do self.fd.close(self.loop_) |_,_| {
+                    let scheduler = Local::take::<Scheduler>();
+                    scheduler.resume_blocked_task_immediately(task_cell.take());
+                };
+            };
+        }
+    }
+}
+
+impl RtioFileStream for UvFileStream {
+    fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
+        self.base_read(buf, -1)
+    }
+    fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
+        self.base_write(buf, -1)
+    }
+    fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
+        self.base_read(buf, offset as i64)
+    }
+    fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> {
+        self.base_write(buf, offset as i64)
+    }
+    fn seek(&mut self, pos: i64, whence: i64) -> Result<(), IoError> {
+        #[fixed_stack_segment]; #[inline(never)];
+        unsafe {
+            match lseek((*self.fd), pos as c_long, whence as c_int) {
+                -1 => {
+                    Err(IoError {
+                        kind: OtherIoError,
+                        desc: "Failed to lseek.",
+                        detail: None
+                    })
+                },
+                _ => Ok(())
+            }
+        }
+    }
+    fn tell(&self) -> Result<u64, IoError> {
+        #[fixed_stack_segment]; #[inline(never)];
+        unsafe {
+            match lseek((*self.fd), 0, SEEK_CUR) {
+                -1 => {
+                    Err(IoError {
+                        kind: OtherIoError,
+                        desc: "Failed to lseek, needed to tell().",
+                        detail: None
+                    })
+                },
+                n=> Ok(n as u64)
+            }
+        }
+    }
+    fn flush(&mut self) -> Result<(), IoError> {
+        Ok(())
+    }
+}
+
 #[test]
 fn test_simple_io_no_connect() {
     do run_in_newsched_task {
@@ -1647,12 +1694,12 @@ fn file_test_uvio_full_simple_impl() {
         {
             let mut fd = (*io).fs_open(&Path(path), create_flags as int, mode as int).unwrap();
             let write_buf = write_val.as_bytes();
-            fd.write(write_buf, 0);
+            fd.write(write_buf);
         }
         {
             let mut fd = (*io).fs_open(&Path(path), ro_flags as int, mode as int).unwrap();
             let mut read_vec = [0, .. 1028];
-            let nread = fd.read(read_vec, 0).unwrap();
+            let nread = fd.read(read_vec).unwrap();
             let read_val = str::from_bytes(read_vec.slice(0, nread as uint));
             assert!(read_val == write_val.to_owned());
         }