diff options
| author | Jeff Olson <olson.jeffery@gmail.com> | 2013-08-20 15:38:41 -0700 |
|---|---|---|
| committer | Jeff Olson <olson.jeffery@gmail.com> | 2013-08-22 16:31:57 -0700 |
| commit | 05c8cc70c90032d578606e811bbb1266cd8d2fcd (patch) | |
| tree | 26d35271abb9c78c159499477956fe2aba0d60c9 /src/libstd | |
| parent | 48d67610285b423e806e0c44be43d01d5c1a7dbc (diff) | |
| download | rust-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.rs | 140 | ||||
| -rw-r--r-- | src/libstd/rt/io/mod.rs | 1 | ||||
| -rw-r--r-- | src/libstd/rt/rtio.rs | 15 | ||||
| -rw-r--r-- | src/libstd/rt/uv/uvio.rs | 111 |
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()); } |
