1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 use super::support::PathLike;
13 use super::{Reader, Writer, Seek};
14 use super::{SeekSet, SeekCur, SeekEnd, SeekStyle};
15 use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject};
16 use rt::io::{io_error, read_error, EndOfFile,
17 FileMode, FileAccess, Open, Read, Create, ReadWrite};
21 /// Open a file for reading/writing, as indicated by `path`.
22 pub fn open<P: PathLike>(path: &P,
25 ) -> Option<FileStream> {
26 let open_result = unsafe {
27 let io: *mut IoFactoryObject = Local::unsafe_borrow();
28 (*io).fs_open(path, mode, access)
31 Ok(fd) => Some(FileStream {
36 io_error::cond.raise(ioerr);
42 /// Unlink (remove) a file from the filesystem, as indicated
44 pub fn unlink<P: PathLike>(path: &P) {
45 let unlink_result = unsafe {
46 let io: *mut IoFactoryObject = Local::unsafe_borrow();
52 io_error::cond.raise(ioerr);
57 /// Abstraction representing *positional* access to a file. In this case,
58 /// *positional* refers to it keeping an encounter *cursor* of where in the
59 /// file a subsequent `read` or `write` will begin from. Users of a `FileStream`
60 /// can `seek` to move the cursor to a given location *within the bounds of the
61 /// file* and can ask to have the `FileStream` `tell` them the location, in
62 /// bytes, of the cursor.
64 /// This abstraction is roughly modeled on the access workflow as represented
65 /// by `open(2)`, `read(2)`, `write(2)` and friends.
67 /// The `open` and `unlink` static methods are provided to manage creation/removal
68 /// of files. All other methods operatin on an instance of `FileStream`.
69 pub struct FileStream {
74 impl Reader for FileStream {
75 fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
76 match self.fd.read(buf) {
78 self.last_nread = read;
81 _ => Some(read as uint)
85 // EOF is indicated by returning None
86 if ioerr.kind != EndOfFile {
87 read_error::cond.raise(ioerr);
94 fn eof(&mut self) -> bool {
99 impl Writer for FileStream {
100 fn write(&mut self, buf: &[u8]) {
101 match self.fd.write(buf) {
104 io_error::cond.raise(ioerr);
109 fn flush(&mut self) {
110 match self.fd.flush() {
113 read_error::cond.raise(ioerr);
119 impl Seek for FileStream {
120 fn tell(&self) -> u64 {
121 let res = self.fd.tell();
123 Ok(cursor) => cursor,
125 read_error::cond.raise(ioerr);
131 fn seek(&mut self, pos: i64, style: SeekStyle) {
132 match self.fd.seek(pos, style) {
134 // successful seek resets EOF indicator
135 self.last_nread = -1;
139 read_error::cond.raise(ioerr);
145 fn file_test_smoke_test_impl() {
146 do run_in_newsched_task {
147 let message = "it's alright. have a good time";
148 let filename = &Path("./tmp/file_rt_io_file_test.txt");
150 let mut write_stream = open(filename, Create, ReadWrite).unwrap();
151 write_stream.write(message.as_bytes());
155 let mut read_stream = open(filename, Open, Read).unwrap();
156 let mut read_buf = [0, .. 1028];
157 let read_str = match read_stream.read(read_buf).unwrap() {
158 -1|0 => fail!("shouldn't happen"),
159 n => str::from_bytes(read_buf.slice_to(n))
161 assert!(read_str == message.to_owned());
168 fn file_test_io_smoke_test() {
169 file_test_smoke_test_impl();
172 fn file_test_invalid_path_opened_without_create_should_raise_condition_impl() {
173 do run_in_newsched_task {
174 let filename = &Path("./tmp/file_that_does_not_exist.txt");
175 let mut called = false;
176 do io_error::cond.trap(|_| {
179 let result = open(filename, Open, Read);
180 assert!(result.is_none());
186 fn file_test_io_invalid_path_opened_without_create_should_raise_condition() {
187 file_test_invalid_path_opened_without_create_should_raise_condition_impl();
190 fn file_test_unlinking_invalid_path_should_raise_condition_impl() {
191 do run_in_newsched_task {
192 let filename = &Path("./tmp/file_another_file_that_does_not_exist.txt");
193 let mut called = false;
194 do io_error::cond.trap(|_| {
203 fn file_test_iounlinking_invalid_path_should_raise_condition() {
204 file_test_unlinking_invalid_path_should_raise_condition_impl();
207 fn file_test_io_non_positional_read_impl() {
208 do run_in_newsched_task {
210 let message = "ten-four";
211 let mut read_mem = [0, .. 8];
212 let filename = &Path("./tmp/file_rt_io_file_test_positional.txt");
214 let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
215 rw_stream.write(message.as_bytes());
218 let mut read_stream = open(filename, Open, Read).unwrap();
220 let read_buf = read_mem.mut_slice(0, 4);
221 read_stream.read(read_buf);
224 let read_buf = read_mem.mut_slice(4, 8);
225 read_stream.read(read_buf);
229 let read_str = str::from_bytes(read_mem);
230 assert!(read_str == message.to_owned());
235 fn file_test_io_non_positional_read() {
236 file_test_io_non_positional_read_impl();
239 fn file_test_io_seeking_impl() {
240 do run_in_newsched_task {
242 let message = "ten-four";
243 let mut read_mem = [0, .. 4];
244 let set_cursor = 4 as u64;
245 let mut tell_pos_pre_read;
246 let mut tell_pos_post_read;
247 let filename = &Path("./tmp/file_rt_io_file_test_seeking.txt");
249 let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
250 rw_stream.write(message.as_bytes());
253 let mut read_stream = open(filename, Open, Read).unwrap();
254 read_stream.seek(set_cursor as i64, SeekSet);
255 tell_pos_pre_read = read_stream.tell();
256 read_stream.read(read_mem);
257 tell_pos_post_read = read_stream.tell();
260 let read_str = str::from_bytes(read_mem);
261 assert!(read_str == message.slice(4, 8).to_owned());
262 assert!(tell_pos_pre_read == set_cursor);
263 assert!(tell_pos_post_read == message.len() as u64);
267 fn file_test_io_seek_and_tell_smoke_test() {
268 file_test_io_seeking_impl();
271 fn file_test_io_seek_and_write_impl() {
273 do run_in_newsched_task {
275 let initial_msg = "food-is-yummy";
276 let overwrite_msg = "-the-bar!!";
277 let final_msg = "foo-the-bar!!";
279 let mut read_mem = [0, .. 13];
280 let filename = &Path("./tmp/file_rt_io_file_test_seek_and_write.txt");
282 let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
283 rw_stream.write(initial_msg.as_bytes());
284 rw_stream.seek(seek_idx as i64, SeekSet);
285 rw_stream.write(overwrite_msg.as_bytes());
288 let mut read_stream = open(filename, Open, Read).unwrap();
289 read_stream.read(read_mem);
292 let read_str = str::from_bytes(read_mem);
293 io::println(fmt!("read_str: '%?' final_msg: '%?'", read_str, final_msg));
294 assert!(read_str == final_msg.to_owned());
298 fn file_test_io_seek_and_write() {
299 file_test_io_seek_and_write_impl();
302 fn file_test_io_seek_shakedown_impl() {
303 do run_in_newsched_task {
304 use str; // 01234567890123
305 let initial_msg = "qwer-asdf-zxcv";
306 let chunk_one = "qwer";
307 let chunk_two = "asdf";
308 let chunk_three = "zxcv";
309 let mut read_mem = [0, .. 4];
310 let filename = &Path("./tmp/file_rt_io_file_test_seek_shakedown.txt");
312 let mut rw_stream = open(filename, Create, ReadWrite).unwrap();
313 rw_stream.write(initial_msg.as_bytes());
316 let mut read_stream = open(filename, Open, Read).unwrap();
318 read_stream.seek(-4, SeekEnd);
319 read_stream.read(read_mem);
320 let read_str = str::from_bytes(read_mem);
321 assert!(read_str == chunk_three.to_owned());
323 read_stream.seek(-9, SeekCur);
324 read_stream.read(read_mem);
325 let read_str = str::from_bytes(read_mem);
326 assert!(read_str == chunk_two.to_owned());
328 read_stream.seek(0, SeekSet);
329 read_stream.read(read_mem);
330 let read_str = str::from_bytes(read_mem);
331 assert!(read_str == chunk_one.to_owned());
337 fn file_test_io_seek_shakedown() {
338 file_test_io_seek_shakedown_impl();