about summary refs log tree commit diff
path: root/src/libstd/sys/common
diff options
context:
space:
mode:
authorAlisdair Owens <awo101@zepler.net>2015-07-10 17:34:07 +0100
committerAlisdair Owens <awo101@zepler.net>2015-07-15 21:30:18 +0100
commit98f287240ff9518c1ea5519c5cd03dc2ba6d4452 (patch)
treeccf70108ba915d7280d99035f647a663ae0c1711 /src/libstd/sys/common
parente4e93196e16030ebf7a20c473849534235d676f8 (diff)
downloadrust-98f287240ff9518c1ea5519c5cd03dc2ba6d4452.tar.gz
rust-98f287240ff9518c1ea5519c5cd03dc2ba6d4452.zip
Add specializations of read_to_end for Stdin, TcpStream and File,
allowing them to read into a buffer containing uninitialized data,
rather than pay the cost of zeroing.
Diffstat (limited to 'src/libstd/sys/common')
-rw-r--r--src/libstd/sys/common/io.rs139
-rw-r--r--src/libstd/sys/common/mod.rs1
2 files changed, 140 insertions, 0 deletions
diff --git a/src/libstd/sys/common/io.rs b/src/libstd/sys/common/io.rs
new file mode 100644
index 00000000000..151d853fc9f
--- /dev/null
+++ b/src/libstd/sys/common/io.rs
@@ -0,0 +1,139 @@
+// 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 prelude::v1::*;
+use io;
+use io::ErrorKind;
+use io::Read;
+use slice::from_raw_parts_mut;
+
+// 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)]
+mod tests {
+    use prelude::v1::*;
+    use io::prelude::*;
+    use super::*;
+    use io;
+    use io::{ErrorKind, Take, Repeat, repeat};
+    use test;
+    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]
+    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/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs
index b528575bbed..69c54f98917 100644
--- a/src/libstd/sys/common/mod.rs
+++ b/src/libstd/sys/common/mod.rs
@@ -16,6 +16,7 @@ pub mod backtrace;
 pub mod condvar;
 pub mod mutex;
 pub mod net;
+pub mod io;
 pub mod poison;
 pub mod remutex;
 pub mod rwlock;