Implement io::native::file
This commit is contained in:
parent
4fd7f852e1
commit
b509f7905a
@ -10,68 +10,273 @@
|
||||
|
||||
//! Blocking posix-based file I/O
|
||||
|
||||
#[allow(non_camel_case_types)];
|
||||
|
||||
use libc;
|
||||
use os;
|
||||
use prelude::*;
|
||||
use super::super::*;
|
||||
use libc::{c_int, FILE};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type fd_t = c_int;
|
||||
fn raise_error() {
|
||||
// XXX: this should probably be a bit more descriptive...
|
||||
let (kind, desc) = match os::errno() as i32 {
|
||||
libc::EOF => (EndOfFile, "end of file"),
|
||||
_ => (OtherIoError, "unknown error"),
|
||||
};
|
||||
|
||||
io_error::cond.raise(IoError {
|
||||
kind: kind,
|
||||
desc: desc,
|
||||
detail: Some(os::last_os_error())
|
||||
});
|
||||
}
|
||||
|
||||
fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 {
|
||||
#[cfg(windows)] static eintr: int = 0; // doesn't matter
|
||||
#[cfg(not(windows))] static eintr: int = libc::EINTR as int;
|
||||
|
||||
let (data, origamt) = do data.as_imm_buf |data, amt| { (data, amt) };
|
||||
let mut data = data;
|
||||
let mut amt = origamt;
|
||||
while amt > 0 {
|
||||
let mut ret;
|
||||
loop {
|
||||
ret = f(data, amt);
|
||||
if cfg!(not(windows)) { break } // windows has no eintr
|
||||
// if we get an eintr, then try again
|
||||
if ret != -1 || os::errno() as int != eintr { break }
|
||||
}
|
||||
if ret == 0 {
|
||||
break
|
||||
} else if ret != -1 {
|
||||
amt -= ret as uint;
|
||||
data = unsafe { data.offset(ret as int) };
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return (origamt - amt) as i64;
|
||||
}
|
||||
|
||||
pub type fd_t = libc::c_int;
|
||||
|
||||
pub struct FileDesc {
|
||||
priv fd: fd_t
|
||||
priv fd: fd_t,
|
||||
}
|
||||
|
||||
impl FileDesc {
|
||||
/// Create a `FileDesc` from an open C file descriptor.
|
||||
///
|
||||
/// The `FileDesc` takes ownership of the file descriptor
|
||||
/// and will close it upon destruction.
|
||||
pub fn new(_fd: fd_t) -> FileDesc { fail2!() }
|
||||
/// The `FileDesc` will take ownership of the specified file descriptor and
|
||||
/// close it upon destruction.
|
||||
///
|
||||
/// Note that all I/O operations done on this object will be *blocking*, but
|
||||
/// they do not require the runtime to be active.
|
||||
pub fn new(fd: fd_t) -> FileDesc {
|
||||
FileDesc { fd: fd }
|
||||
}
|
||||
}
|
||||
|
||||
impl Reader for FileDesc {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
|
||||
#[cfg(windows)] type rlen = libc::c_uint;
|
||||
#[cfg(not(windows))] type rlen = libc::size_t;
|
||||
let ret = do keep_going(buf) |buf, len| {
|
||||
unsafe {
|
||||
libc::read(self.fd, buf as *mut libc::c_void, len as rlen) as i64
|
||||
}
|
||||
};
|
||||
if ret == 0 {
|
||||
None
|
||||
} else if ret < 0 {
|
||||
raise_error();
|
||||
None
|
||||
} else {
|
||||
Some(ret as uint)
|
||||
}
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> bool { fail2!() }
|
||||
fn eof(&mut self) -> bool { false }
|
||||
}
|
||||
|
||||
impl Writer for FileDesc {
|
||||
fn write(&mut self, _buf: &[u8]) { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
#[cfg(windows)] type wlen = libc::c_uint;
|
||||
#[cfg(not(windows))] type wlen = libc::size_t;
|
||||
let ret = do keep_going(buf) |buf, len| {
|
||||
unsafe {
|
||||
libc::write(self.fd, buf as *libc::c_void, len as wlen) as i64
|
||||
}
|
||||
};
|
||||
if ret < 0 {
|
||||
raise_error();
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) { fail2!() }
|
||||
fn flush(&mut self) {}
|
||||
}
|
||||
|
||||
impl Seek for FileDesc {
|
||||
fn tell(&self) -> u64 { fail2!() }
|
||||
|
||||
fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() }
|
||||
impl Drop for FileDesc {
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::close(self.fd); }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CFile {
|
||||
priv file: *FILE
|
||||
priv file: *libc::FILE
|
||||
}
|
||||
|
||||
impl CFile {
|
||||
/// Create a `CFile` from an open `FILE` pointer.
|
||||
///
|
||||
/// The `CFile` takes ownership of the file descriptor
|
||||
/// and will close it upon destruction.
|
||||
pub fn new(_file: *FILE) -> CFile { fail2!() }
|
||||
/// The `CFile` takes ownership of the `FILE` pointer and will close it upon
|
||||
/// destruction.
|
||||
pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } }
|
||||
}
|
||||
|
||||
impl Reader for CFile {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
|
||||
let ret = do keep_going(buf) |buf, len| {
|
||||
unsafe {
|
||||
libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t,
|
||||
self.file) as i64
|
||||
}
|
||||
};
|
||||
if ret == 0 {
|
||||
None
|
||||
} else if ret < 0 {
|
||||
raise_error();
|
||||
None
|
||||
} else {
|
||||
Some(ret as uint)
|
||||
}
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> bool { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn eof(&mut self) -> bool {
|
||||
unsafe { libc::feof(self.file) != 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer for CFile {
|
||||
fn write(&mut self, _buf: &[u8]) { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
let ret = do keep_going(buf) |buf, len| {
|
||||
unsafe {
|
||||
libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t,
|
||||
self.file) as i64
|
||||
}
|
||||
};
|
||||
if ret < 0 {
|
||||
raise_error();
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn flush(&mut self) {
|
||||
if unsafe { libc::fflush(self.file) } < 0 {
|
||||
raise_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for CFile {
|
||||
fn tell(&self) -> u64 { fail2!() }
|
||||
fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn tell(&self) -> u64 {
|
||||
let ret = unsafe { libc::ftell(self.file) };
|
||||
if ret < 0 {
|
||||
raise_error();
|
||||
}
|
||||
return ret as u64;
|
||||
}
|
||||
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn seek(&mut self, pos: i64, style: SeekStyle) {
|
||||
let whence = match style {
|
||||
SeekSet => libc::SEEK_SET,
|
||||
SeekEnd => libc::SEEK_END,
|
||||
SeekCur => libc::SEEK_CUR,
|
||||
};
|
||||
if unsafe { libc::fseek(self.file, pos as libc::c_long, whence) } < 0 {
|
||||
raise_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CFile {
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::fclose(self.file); }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use libc;
|
||||
use os;
|
||||
use prelude::*;
|
||||
use rt::io::{io_error, SeekSet};
|
||||
use super::*;
|
||||
|
||||
#[test] #[fixed_stack_segment]
|
||||
fn test_file_desc() {
|
||||
// Run this test with some pipes so we don't have to mess around with
|
||||
// opening or closing files.
|
||||
unsafe {
|
||||
let os::Pipe { input, out } = os::pipe();
|
||||
let mut reader = FileDesc::new(input);
|
||||
let mut writer = FileDesc::new(out);
|
||||
|
||||
writer.write(bytes!("test"));
|
||||
let mut buf = [0u8, ..4];
|
||||
match reader.read(buf) {
|
||||
Some(4) => {
|
||||
assert_eq!(buf[0], 't' as u8);
|
||||
assert_eq!(buf[1], 'e' as u8);
|
||||
assert_eq!(buf[2], 's' as u8);
|
||||
assert_eq!(buf[3], 't' as u8);
|
||||
}
|
||||
r => fail2!("invalid read: {:?}", r)
|
||||
}
|
||||
|
||||
let mut raised = false;
|
||||
do io_error::cond.trap(|_| { raised = true; }).inside {
|
||||
writer.read(buf);
|
||||
}
|
||||
assert!(raised);
|
||||
|
||||
raised = false;
|
||||
do io_error::cond.trap(|_| { raised = true; }).inside {
|
||||
reader.write(buf);
|
||||
}
|
||||
assert!(raised);
|
||||
}
|
||||
}
|
||||
|
||||
#[test] #[fixed_stack_segment]
|
||||
#[ignore(windows)] // apparently windows doesn't like tmpfile
|
||||
fn test_cfile() {
|
||||
unsafe {
|
||||
let f = libc::tmpfile();
|
||||
assert!(!f.is_null());
|
||||
let mut file = CFile::new(f);
|
||||
|
||||
file.write(bytes!("test"));
|
||||
let mut buf = [0u8, ..4];
|
||||
file.seek(0, SeekSet);
|
||||
match file.read(buf) {
|
||||
Some(4) => {
|
||||
assert_eq!(buf[0], 't' as u8);
|
||||
assert_eq!(buf[1], 'e' as u8);
|
||||
assert_eq!(buf[2], 's' as u8);
|
||||
assert_eq!(buf[3], 't' as u8);
|
||||
}
|
||||
r => fail2!("invalid read: {:?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user