From 3f2366df139e8bc0323ab7ae287ffc9e25129c6c Mon Sep 17 00:00:00 2001 From: Fenrir Date: Fri, 24 Feb 2017 01:22:17 -0700 Subject: implement buffered stdio --- ctr-std/src/sys/unix/fd.rs | 137 ++++++++++++++++++++++++++++++++++++++++++ ctr-std/src/sys/unix/io.rs | 81 +++++++++++++++++++++++++ ctr-std/src/sys/unix/mod.rs | 2 + ctr-std/src/sys/unix/stdio.rs | 81 +++++++++++++++++++++++++ 4 files changed, 301 insertions(+) create mode 100644 ctr-std/src/sys/unix/fd.rs create mode 100644 ctr-std/src/sys/unix/io.rs create mode 100644 ctr-std/src/sys/unix/stdio.rs (limited to 'ctr-std/src/sys') diff --git a/ctr-std/src/sys/unix/fd.rs b/ctr-std/src/sys/unix/fd.rs new file mode 100644 index 0000000..917a34b --- /dev/null +++ b/ctr-std/src/sys/unix/fd.rs @@ -0,0 +1,137 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(reason = "not public", issue = "0", feature = "fd")] + +use cmp; +use io::{self, Read}; +use libc::{self, c_int, c_void, ssize_t}; +use mem; +use sys::cvt; +use sys_common::AsInner; +use sys_common::io::read_to_end_uninitialized; + +#[derive(Debug)] +pub struct FileDesc { + fd: c_int, +} + +fn max_len() -> usize { + // The maximum read limit on most posix-like systems is `SSIZE_MAX`, + // with the man page quoting that if the count of bytes to read is + // greater than `SSIZE_MAX` the result is "unspecified". + // + // On OSX, however, apparently the 64-bit libc is either buggy or + // intentionally showing odd behavior by rejecting any read with a size + // larger than or equal to INT_MAX. To handle both of these the read + // size is capped on both platforms. + if cfg!(target_os = "macos") { + ::max_value() as usize - 1 + } else { + ::max_value() as usize + } +} + +impl FileDesc { + pub fn new(fd: c_int) -> FileDesc { + FileDesc { fd: fd } + } + + pub fn raw(&self) -> c_int { self.fd } + + /// Extracts the actual filedescriptor without closing it. + pub fn into_raw(self) -> c_int { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let ret = cvt(unsafe { + libc::read(self.fd, + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), max_len())) + })?; + Ok(ret as usize) + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64) + -> io::Result + { + use libc::pread as pread64; + cvt(pread64(fd, buf, count, offset)) + } + + unsafe { + cvt_pread64(self.fd, + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), max_len()), + offset as i64) + .map(|n| n as usize) + } + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let ret = cvt(unsafe { + libc::write(self.fd, + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), max_len())) + })?; + Ok(ret as usize) + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64) + -> io::Result + { + use libc::pwrite as pwrite64; + cvt(pwrite64(fd, buf, count, offset)) + } + + unsafe { + cvt_pwrite64(self.fd, + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), max_len()), + offset as i64) + .map(|n| n as usize) + } + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + unsafe { read_to_end_uninitialized(self, buf) } + } +} + +impl AsInner for FileDesc { + fn as_inner(&self) -> &c_int { &self.fd } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // (opened after we closed ours. + let _ = unsafe { libc::close(self.fd) }; + } +} diff --git a/ctr-std/src/sys/unix/io.rs b/ctr-std/src/sys/unix/io.rs new file mode 100644 index 0000000..6d38b00 --- /dev/null +++ b/ctr-std/src/sys/unix/io.rs @@ -0,0 +1,81 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use libc; +use sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +impl Stdin { + pub fn new() -> io::Result { Ok(Stdin(())) } + + pub fn read(&self, data: &mut [u8]) -> io::Result { + let fd = FileDesc::new(libc::STDIN_FILENO); + let ret = fd.read(data); + fd.into_raw(); + ret + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let fd = FileDesc::new(libc::STDIN_FILENO); + let ret = fd.read_to_end(buf); + fd.into_raw(); + ret + } +} + +impl Stdout { + pub fn new() -> io::Result { Ok(Stdout(())) } + + pub fn write(&self, data: &[u8]) -> io::Result { + let fd = FileDesc::new(libc::STDOUT_FILENO); + let ret = fd.write(data); + fd.into_raw(); + ret + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub fn new() -> io::Result { Ok(Stderr(())) } + + pub fn write(&self, data: &[u8]) -> io::Result { + let fd = FileDesc::new(libc::STDERR_FILENO); + let ret = fd.write(data); + fd.into_raw(); + ret + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +// FIXME: right now this raw stderr handle is used in a few places because +// std::io::stderr_raw isn't exposed, but once that's exposed this impl +// should go away +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result { + Stderr::write(self, data) + } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } +} + +pub const EBADF_ERR: i32 = ::libc::EBADF as i32; +pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; diff --git a/ctr-std/src/sys/unix/mod.rs b/ctr-std/src/sys/unix/mod.rs index ec657d8..0da1d3b 100644 --- a/ctr-std/src/sys/unix/mod.rs +++ b/ctr-std/src/sys/unix/mod.rs @@ -15,6 +15,8 @@ use libc; pub mod ext; pub mod fast_thread_local; +pub mod fd; +pub mod stdio; pub mod memchr; pub mod mutex; pub mod os; diff --git a/ctr-std/src/sys/unix/stdio.rs b/ctr-std/src/sys/unix/stdio.rs new file mode 100644 index 0000000..6d38b00 --- /dev/null +++ b/ctr-std/src/sys/unix/stdio.rs @@ -0,0 +1,81 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use libc; +use sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +impl Stdin { + pub fn new() -> io::Result { Ok(Stdin(())) } + + pub fn read(&self, data: &mut [u8]) -> io::Result { + let fd = FileDesc::new(libc::STDIN_FILENO); + let ret = fd.read(data); + fd.into_raw(); + ret + } + + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let fd = FileDesc::new(libc::STDIN_FILENO); + let ret = fd.read_to_end(buf); + fd.into_raw(); + ret + } +} + +impl Stdout { + pub fn new() -> io::Result { Ok(Stdout(())) } + + pub fn write(&self, data: &[u8]) -> io::Result { + let fd = FileDesc::new(libc::STDOUT_FILENO); + let ret = fd.write(data); + fd.into_raw(); + ret + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub fn new() -> io::Result { Ok(Stderr(())) } + + pub fn write(&self, data: &[u8]) -> io::Result { + let fd = FileDesc::new(libc::STDERR_FILENO); + let ret = fd.write(data); + fd.into_raw(); + ret + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +// FIXME: right now this raw stderr handle is used in a few places because +// std::io::stderr_raw isn't exposed, but once that's exposed this impl +// should go away +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result { + Stderr::write(self, data) + } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } +} + +pub const EBADF_ERR: i32 = ::libc::EBADF as i32; +pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; -- cgit v1.2.3