|
10 | 10 |
|
11 | 11 | //! Blocking posix-based file I/O
|
12 | 12 |
|
| 13 | +#[allow(non_camel_case_types)]; |
| 14 | + |
| 15 | +use libc; |
| 16 | +use os; |
13 | 17 | use prelude::*;
|
14 | 18 | use super::super::*;
|
15 |
| -use libc::{c_int, FILE}; |
16 | 19 |
|
17 |
| -#[allow(non_camel_case_types)] |
18 |
| -pub type fd_t = c_int; |
| 20 | +fn raise_error() { |
| 21 | + // XXX: this should probably be a bit more descriptive... |
| 22 | + let (kind, desc) = match os::errno() as i32 { |
| 23 | + libc::EOF => (EndOfFile, "end of file"), |
| 24 | + _ => (OtherIoError, "unknown error"), |
| 25 | + }; |
| 26 | + |
| 27 | + io_error::cond.raise(IoError { |
| 28 | + kind: kind, |
| 29 | + desc: desc, |
| 30 | + detail: Some(os::last_os_error()) |
| 31 | + }); |
| 32 | +} |
| 33 | + |
| 34 | +fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 { |
| 35 | + #[cfg(windows)] static eintr: int = 0; // doesn't matter |
| 36 | + #[cfg(not(windows))] static eintr: int = libc::EINTR as int; |
| 37 | + |
| 38 | + let (data, origamt) = do data.as_imm_buf |data, amt| { (data, amt) }; |
| 39 | + let mut data = data; |
| 40 | + let mut amt = origamt; |
| 41 | + while amt > 0 { |
| 42 | + let mut ret; |
| 43 | + loop { |
| 44 | + ret = f(data, amt); |
| 45 | + if cfg!(not(windows)) { break } // windows has no eintr |
| 46 | + // if we get an eintr, then try again |
| 47 | + if ret != -1 || os::errno() as int != eintr { break } |
| 48 | + } |
| 49 | + if ret == 0 { |
| 50 | + break |
| 51 | + } else if ret != -1 { |
| 52 | + amt -= ret as uint; |
| 53 | + data = unsafe { data.offset(ret as int) }; |
| 54 | + } else { |
| 55 | + return ret; |
| 56 | + } |
| 57 | + } |
| 58 | + return (origamt - amt) as i64; |
| 59 | +} |
| 60 | + |
| 61 | +pub type fd_t = libc::c_int; |
19 | 62 |
|
20 | 63 | pub struct FileDesc {
|
21 |
| - priv fd: fd_t |
| 64 | + priv fd: fd_t, |
22 | 65 | }
|
23 | 66 |
|
24 | 67 | impl FileDesc {
|
25 | 68 | /// Create a `FileDesc` from an open C file descriptor.
|
26 | 69 | ///
|
27 |
| - /// The `FileDesc` takes ownership of the file descriptor |
28 |
| - /// and will close it upon destruction. |
29 |
| - pub fn new(_fd: fd_t) -> FileDesc { fail2!() } |
| 70 | + /// The `FileDesc` will take ownership of the specified file descriptor and |
| 71 | + /// close it upon destruction. |
| 72 | + /// |
| 73 | + /// Note that all I/O operations done on this object will be *blocking*, but |
| 74 | + /// they do not require the runtime to be active. |
| 75 | + pub fn new(fd: fd_t) -> FileDesc { |
| 76 | + FileDesc { fd: fd } |
| 77 | + } |
30 | 78 | }
|
31 | 79 |
|
32 | 80 | impl Reader for FileDesc {
|
33 |
| - fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail2!() } |
| 81 | + #[fixed_stack_segment] #[inline(never)] |
| 82 | + fn read(&mut self, buf: &mut [u8]) -> Option<uint> { |
| 83 | + #[cfg(windows)] type rlen = libc::c_uint; |
| 84 | + #[cfg(not(windows))] type rlen = libc::size_t; |
| 85 | + let ret = do keep_going(buf) |buf, len| { |
| 86 | + unsafe { |
| 87 | + libc::read(self.fd, buf as *mut libc::c_void, len as rlen) as i64 |
| 88 | + } |
| 89 | + }; |
| 90 | + if ret == 0 { |
| 91 | + None |
| 92 | + } else if ret < 0 { |
| 93 | + raise_error(); |
| 94 | + None |
| 95 | + } else { |
| 96 | + Some(ret as uint) |
| 97 | + } |
| 98 | + } |
34 | 99 |
|
35 |
| - fn eof(&mut self) -> bool { fail2!() } |
| 100 | + fn eof(&mut self) -> bool { false } |
36 | 101 | }
|
37 | 102 |
|
38 | 103 | impl Writer for FileDesc {
|
39 |
| - fn write(&mut self, _buf: &[u8]) { fail2!() } |
| 104 | + #[fixed_stack_segment] #[inline(never)] |
| 105 | + fn write(&mut self, buf: &[u8]) { |
| 106 | + #[cfg(windows)] type wlen = libc::c_uint; |
| 107 | + #[cfg(not(windows))] type wlen = libc::size_t; |
| 108 | + let ret = do keep_going(buf) |buf, len| { |
| 109 | + unsafe { |
| 110 | + libc::write(self.fd, buf as *libc::c_void, len as wlen) as i64 |
| 111 | + } |
| 112 | + }; |
| 113 | + if ret < 0 { |
| 114 | + raise_error(); |
| 115 | + } |
| 116 | + } |
40 | 117 |
|
41 |
| - fn flush(&mut self) { fail2!() } |
| 118 | + fn flush(&mut self) {} |
42 | 119 | }
|
43 | 120 |
|
44 |
| -impl Seek for FileDesc { |
45 |
| - fn tell(&self) -> u64 { fail2!() } |
46 |
| - |
47 |
| - fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() } |
| 121 | +impl Drop for FileDesc { |
| 122 | + #[fixed_stack_segment] #[inline(never)] |
| 123 | + fn drop(&mut self) { |
| 124 | + unsafe { libc::close(self.fd); } |
| 125 | + } |
48 | 126 | }
|
49 | 127 |
|
50 | 128 | pub struct CFile {
|
51 |
| - priv file: *FILE |
| 129 | + priv file: *libc::FILE |
52 | 130 | }
|
53 | 131 |
|
54 | 132 | impl CFile {
|
55 | 133 | /// Create a `CFile` from an open `FILE` pointer.
|
56 | 134 | ///
|
57 |
| - /// The `CFile` takes ownership of the file descriptor |
58 |
| - /// and will close it upon destruction. |
59 |
| - pub fn new(_file: *FILE) -> CFile { fail2!() } |
| 135 | + /// The `CFile` takes ownership of the `FILE` pointer and will close it upon |
| 136 | + /// destruction. |
| 137 | + pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } } |
60 | 138 | }
|
61 | 139 |
|
62 | 140 | impl Reader for CFile {
|
63 |
| - fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail2!() } |
| 141 | + #[fixed_stack_segment] #[inline(never)] |
| 142 | + fn read(&mut self, buf: &mut [u8]) -> Option<uint> { |
| 143 | + let ret = do keep_going(buf) |buf, len| { |
| 144 | + unsafe { |
| 145 | + libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t, |
| 146 | + self.file) as i64 |
| 147 | + } |
| 148 | + }; |
| 149 | + if ret == 0 { |
| 150 | + None |
| 151 | + } else if ret < 0 { |
| 152 | + raise_error(); |
| 153 | + None |
| 154 | + } else { |
| 155 | + Some(ret as uint) |
| 156 | + } |
| 157 | + } |
64 | 158 |
|
65 |
| - fn eof(&mut self) -> bool { fail2!() } |
| 159 | + #[fixed_stack_segment] #[inline(never)] |
| 160 | + fn eof(&mut self) -> bool { |
| 161 | + unsafe { libc::feof(self.file) != 0 } |
| 162 | + } |
66 | 163 | }
|
67 | 164 |
|
68 | 165 | impl Writer for CFile {
|
69 |
| - fn write(&mut self, _buf: &[u8]) { fail2!() } |
| 166 | + #[fixed_stack_segment] #[inline(never)] |
| 167 | + fn write(&mut self, buf: &[u8]) { |
| 168 | + let ret = do keep_going(buf) |buf, len| { |
| 169 | + unsafe { |
| 170 | + libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t, |
| 171 | + self.file) as i64 |
| 172 | + } |
| 173 | + }; |
| 174 | + if ret < 0 { |
| 175 | + raise_error(); |
| 176 | + } |
| 177 | + } |
70 | 178 |
|
71 |
| - fn flush(&mut self) { fail2!() } |
| 179 | + #[fixed_stack_segment] #[inline(never)] |
| 180 | + fn flush(&mut self) { |
| 181 | + if unsafe { libc::fflush(self.file) } < 0 { |
| 182 | + raise_error(); |
| 183 | + } |
| 184 | + } |
72 | 185 | }
|
73 | 186 |
|
74 | 187 | impl Seek for CFile {
|
75 |
| - fn tell(&self) -> u64 { fail2!() } |
76 |
| - fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() } |
| 188 | + #[fixed_stack_segment] #[inline(never)] |
| 189 | + fn tell(&self) -> u64 { |
| 190 | + let ret = unsafe { libc::ftell(self.file) }; |
| 191 | + if ret < 0 { |
| 192 | + raise_error(); |
| 193 | + } |
| 194 | + return ret as u64; |
| 195 | + } |
| 196 | + |
| 197 | + #[fixed_stack_segment] #[inline(never)] |
| 198 | + fn seek(&mut self, pos: i64, style: SeekStyle) { |
| 199 | + let whence = match style { |
| 200 | + SeekSet => libc::SEEK_SET, |
| 201 | + SeekEnd => libc::SEEK_END, |
| 202 | + SeekCur => libc::SEEK_CUR, |
| 203 | + }; |
| 204 | + if unsafe { libc::fseek(self.file, pos as libc::c_long, whence) } < 0 { |
| 205 | + raise_error(); |
| 206 | + } |
| 207 | + } |
| 208 | +} |
| 209 | + |
| 210 | +impl Drop for CFile { |
| 211 | + #[fixed_stack_segment] #[inline(never)] |
| 212 | + fn drop(&mut self) { |
| 213 | + unsafe { libc::fclose(self.file); } |
| 214 | + } |
| 215 | +} |
| 216 | + |
| 217 | +#[cfg(test)] |
| 218 | +mod tests { |
| 219 | + use libc; |
| 220 | + use os; |
| 221 | + use prelude::*; |
| 222 | + use rt::io::{io_error, SeekSet}; |
| 223 | + use super::*; |
| 224 | + |
| 225 | + #[test] #[fixed_stack_segment] |
| 226 | + fn test_file_desc() { |
| 227 | + // Run this test with some pipes so we don't have to mess around with |
| 228 | + // opening or closing files. |
| 229 | + unsafe { |
| 230 | + let os::Pipe { input, out } = os::pipe(); |
| 231 | + let mut reader = FileDesc::new(input); |
| 232 | + let mut writer = FileDesc::new(out); |
| 233 | + |
| 234 | + writer.write(bytes!("test")); |
| 235 | + let mut buf = [0u8, ..4]; |
| 236 | + match reader.read(buf) { |
| 237 | + Some(4) => { |
| 238 | + assert_eq!(buf[0], 't' as u8); |
| 239 | + assert_eq!(buf[1], 'e' as u8); |
| 240 | + assert_eq!(buf[2], 's' as u8); |
| 241 | + assert_eq!(buf[3], 't' as u8); |
| 242 | + } |
| 243 | + r => fail2!("invalid read: {:?}", r) |
| 244 | + } |
| 245 | + |
| 246 | + let mut raised = false; |
| 247 | + do io_error::cond.trap(|_| { raised = true; }).inside { |
| 248 | + writer.read(buf); |
| 249 | + } |
| 250 | + assert!(raised); |
| 251 | + |
| 252 | + raised = false; |
| 253 | + do io_error::cond.trap(|_| { raised = true; }).inside { |
| 254 | + reader.write(buf); |
| 255 | + } |
| 256 | + assert!(raised); |
| 257 | + } |
| 258 | + } |
| 259 | + |
| 260 | + #[test] #[fixed_stack_segment] |
| 261 | + #[ignore(windows)] // apparently windows doesn't like tmpfile |
| 262 | + fn test_cfile() { |
| 263 | + unsafe { |
| 264 | + let f = libc::tmpfile(); |
| 265 | + assert!(!f.is_null()); |
| 266 | + let mut file = CFile::new(f); |
| 267 | + |
| 268 | + file.write(bytes!("test")); |
| 269 | + let mut buf = [0u8, ..4]; |
| 270 | + file.seek(0, SeekSet); |
| 271 | + match file.read(buf) { |
| 272 | + Some(4) => { |
| 273 | + assert_eq!(buf[0], 't' as u8); |
| 274 | + assert_eq!(buf[1], 'e' as u8); |
| 275 | + assert_eq!(buf[2], 's' as u8); |
| 276 | + assert_eq!(buf[3], 't' as u8); |
| 277 | + } |
| 278 | + r => fail2!("invalid read: {:?}", r) |
| 279 | + } |
| 280 | + } |
| 281 | + } |
77 | 282 | }
|
0 commit comments