diff options
| author | Ronald Kinard <[email protected]> | 2017-01-28 23:12:35 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2017-01-28 23:12:35 -0600 |
| commit | 20a7da8cfae69cbd51b26203bca036a700ba79de (patch) | |
| tree | 9677e765df77c7a9d11bcaf21c40cb33a4c6bd08 /ctr-std/src/sys_common | |
| parent | Update AUTHORS.md (diff) | |
| parent | ctru-rs: impl Seek for File (diff) | |
| download | ctru-rs-20a7da8cfae69cbd51b26203bca036a700ba79de.tar.xz ctru-rs-20a7da8cfae69cbd51b26203bca036a700ba79de.zip | |
Merge pull request #15 from FenrirWolf/std
Standard library support, for reals this time.
Diffstat (limited to 'ctr-std/src/sys_common')
| -rw-r--r-- | ctr-std/src/sys_common/io.rs | 179 | ||||
| -rw-r--r-- | ctr-std/src/sys_common/mod.rs | 76 |
2 files changed, 255 insertions, 0 deletions
diff --git a/ctr-std/src/sys_common/io.rs b/ctr-std/src/sys_common/io.rs new file mode 100644 index 0000000..23daeeb --- /dev/null +++ b/ctr-std/src/sys_common/io.rs @@ -0,0 +1,179 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use io; +use io::ErrorKind; +use io::Read; +use slice::from_raw_parts_mut; + +pub const DEFAULT_BUF_SIZE: usize = 8 * 1024; + +// Provides read_to_end functionality over an uninitialized buffer. +// This function is unsafe because it calls the underlying +// read function with a slice into uninitialized memory. The default +// implementation of read_to_end for readers will zero out new memory in +// the buf before passing it to read, but avoiding this zero can often +// lead to a fairly significant performance win. +// +// Implementations using this method have to adhere to two guarantees: +// * The implementation of read never reads the buffer provided. +// * The implementation of read correctly reports how many bytes were written. +pub unsafe fn read_to_end_uninitialized(r: &mut Read, buf: &mut Vec<u8>) -> io::Result<usize> { + + let start_len = buf.len(); + buf.reserve(16); + + // Always try to read into the empty space of the vector (from the length to the capacity). + // If the vector ever fills up then we reserve an extra byte which should trigger the normal + // reallocation routines for the vector, which will likely double the size. + // + // This function is similar to the read_to_end function in std::io, but the logic about + // reservations and slicing is different enough that this is duplicated here. + loop { + if buf.len() == buf.capacity() { + buf.reserve(1); + } + + let buf_slice = from_raw_parts_mut(buf.as_mut_ptr().offset(buf.len() as isize), + buf.capacity() - buf.len()); + + match r.read(buf_slice) { + Ok(0) => { return Ok(buf.len() - start_len); } + Ok(n) => { let len = buf.len() + n; buf.set_len(len); }, + Err(ref e) if e.kind() == ErrorKind::Interrupted => { } + Err(e) => { return Err(e); } + } + } +} + +#[cfg(test)] +#[allow(dead_code)] // not used on emscripten +pub mod test { + use path::{Path, PathBuf}; + use env; + use rand::{self, Rng}; + use fs; + + pub struct TempDir(PathBuf); + + impl TempDir { + pub fn join(&self, path: &str) -> PathBuf { + let TempDir(ref p) = *self; + p.join(path) + } + + pub fn path<'a>(&'a self) -> &'a Path { + let TempDir(ref p) = *self; + p + } + } + + impl Drop for TempDir { + fn drop(&mut self) { + // Gee, seeing how we're testing the fs module I sure hope that we + // at least implement this correctly! + let TempDir(ref p) = *self; + fs::remove_dir_all(p).unwrap(); + } + } + + pub fn tmpdir() -> TempDir { + let p = env::temp_dir(); + let mut r = rand::thread_rng(); + let ret = p.join(&format!("rust-{}", r.next_u32())); + fs::create_dir(&ret).unwrap(); + TempDir(ret) + } +} + +#[cfg(test)] +mod tests { + use io::prelude::*; + use super::*; + use io; + use io::{ErrorKind, Take, Repeat, repeat}; + use slice::from_raw_parts; + + struct ErrorRepeat { + lr: Take<Repeat> + } + + fn error_repeat(byte: u8, limit: u64) -> ErrorRepeat { + ErrorRepeat { lr: repeat(byte).take(limit) } + } + + impl Read for ErrorRepeat { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let ret = self.lr.read(buf); + if let Ok(0) = ret { + return Err(io::Error::new(ErrorKind::Other, "")) + } + ret + } + } + + fn init_vec_data() -> Vec<u8> { + let mut vec = vec![10u8; 200]; + unsafe { vec.set_len(0); } + vec + } + + fn assert_all_eq(buf: &[u8], value: u8) { + for n in buf { + assert_eq!(*n, value); + } + } + + fn validate(buf: &Vec<u8>, good_read_len: usize) { + assert_all_eq(buf, 1u8); + let cap = buf.capacity(); + let end_slice = unsafe { from_raw_parts(buf.as_ptr().offset(good_read_len as isize), + cap - good_read_len) }; + assert_all_eq(end_slice, 10u8); + } + + #[test] + fn read_to_end_uninit_error() { + let mut er = error_repeat(1,100); + let mut vec = init_vec_data(); + if let Err(_) = unsafe { read_to_end_uninitialized(&mut er, &mut vec) } { + validate(&vec, 100); + } else { + assert!(false); + } + } + + #[test] + fn read_to_end_uninit_zero_len_vec() { + let mut er = repeat(1).take(100); + let mut vec = Vec::new(); + let n = unsafe{ read_to_end_uninitialized(&mut er, &mut vec).unwrap() }; + assert_all_eq(&vec, 1u8); + assert_eq!(vec.len(), n); + } + + #[test] + fn read_to_end_uninit_good() { + let mut er = repeat(1).take(100); + let mut vec = init_vec_data(); + let n = unsafe{ read_to_end_uninitialized(&mut er, &mut vec).unwrap() }; + validate(&vec, 100); + assert_eq!(vec.len(), n); + } + + #[bench] + #[cfg_attr(target_os = "emscripten", ignore)] + fn bench_uninitialized(b: &mut ::test::Bencher) { + b.iter(|| { + let mut lr = repeat(1).take(10000000); + let mut vec = Vec::with_capacity(1024); + unsafe { read_to_end_uninitialized(&mut lr, &mut vec) } + }); + } +} diff --git a/ctr-std/src/sys_common/mod.rs b/ctr-std/src/sys_common/mod.rs new file mode 100644 index 0000000..38057b8 --- /dev/null +++ b/ctr-std/src/sys_common/mod.rs @@ -0,0 +1,76 @@ +// Copyright 2014 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Platform-independent platform abstraction +//! +//! This is the platform-independent portion of the standard libraries +//! platform abstraction layer, whereas `std::sys` is the +//! platform-specific portion. +//! +//! The relationship between `std::sys_common`, `std::sys` and the +//! rest of `std` is complex, with dependencies going in all +//! directions: `std` depending on `sys_common`, `sys_common` +//! depending on `sys`, and `sys` depending on `sys_common` and `std`. +//! Ideally `sys_common` would be split into two and the dependencies +//! between them all would form a dag, facilitating the extraction of +//! `std::sys` from the standard library. + +#![allow(missing_docs)] + +pub mod io; + +// common error constructors + +/// A trait for viewing representations from std types +#[doc(hidden)] +pub trait AsInner<Inner: ?Sized> { + fn as_inner(&self) -> &Inner; +} + +/// A trait for viewing representations from std types +#[doc(hidden)] +pub trait AsInnerMut<Inner: ?Sized> { + fn as_inner_mut(&mut self) -> &mut Inner; +} + +/// A trait for extracting representations from std types +#[doc(hidden)] +pub trait IntoInner<Inner> { + fn into_inner(self) -> Inner; +} + +/// A trait for creating std types from internal representations +#[doc(hidden)] +pub trait FromInner<Inner> { + fn from_inner(inner: Inner) -> Self; +} + +macro_rules! rtabort { + ($($t:tt)*) => (::sys_common::util::abort(format_args!($($t)*))) +} + +// Computes (value*numer)/denom without overflow, as long as both +// (numer*denom) and the overall result fit into i64 (which is the case +// for our time conversions). +#[allow(dead_code)] // not used on all platforms +pub fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 { + let q = value / denom; + let r = value % denom; + // Decompose value as (value/denom*denom + value%denom), + // substitute into (value*numer)/denom and simplify. + // r < denom, so (denom*numer) is the upper bound of (r*numer) + q * numer + r * numer / denom +} + +#[test] +fn test_muldiv() { + assert_eq!(mul_div_u64( 1_000_000_000_001, 1_000_000_000, 1_000_000), + 1_000_000_000_001_000); +} |