about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorErick Tryzelaar <erick.tryzelaar@gmail.com>2013-09-18 12:32:35 -0700
committerErick Tryzelaar <erick.tryzelaar@gmail.com>2013-09-26 22:20:40 -0700
commite02d1eb1717fee3c513baa0cb5fc39b8fa630335 (patch)
tree6f21aca82d147d6fba8dbb2000d5acabeefdf923 /src/libstd
parent410a96cc79df4fbb3c2f30bdb2682b867df41a7e (diff)
downloadrust-e02d1eb1717fee3c513baa0cb5fc39b8fa630335.tar.gz
rust-e02d1eb1717fee3c513baa0cb5fc39b8fa630335.zip
std: Micro-optimize vec.with_c_str for short vectors
This now makes it unsafe to save the pointer returned by .with_c_str
as that pointer now may be pointing at a stack allocated array.

I arbitrarily chose 32 bytes as the length of the stack vector, and
so it might not be the most optimal size.

before:

test c_str::bench::bench_with_c_str_long ... bench: 539 ns/iter (+/- 91)
test c_str::bench::bench_with_c_str_medium ... bench: 97 ns/iter (+/- 2)
test c_str::bench::bench_with_c_str_short ... bench: 70 ns/iter (+/- 5)

after:

test c_str::bench::bench_with_c_str_long ... bench: 542 ns/iter (+/- 13)
test c_str::bench::bench_with_c_str_medium ... bench: 53 ns/iter (+/- 6)
test c_str::bench::bench_with_c_str_short ... bench: 19 ns/iter (+/- 0)
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/c_str.rs66
1 files changed, 52 insertions, 14 deletions
diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs
index 3506faadd3e..8b5b780012b 100644
--- a/src/libstd/c_str.rs
+++ b/src/libstd/c_str.rs
@@ -61,16 +61,17 @@ do my_string.with_c_str |c_buffer| {
 */
 
 use cast;
+use container::Container;
 use iter::{Iterator, range};
 use libc;
 use ops::Drop;
 use option::{Option, Some, None};
 use ptr::RawPtr;
 use ptr;
-use str;
 use str::StrSlice;
-use vec::{ImmutableVector, CopyableVector};
-use container::Container;
+use str;
+use vec::{CopyableVector, ImmutableVector, MutableVector};
+use unstable::intrinsics;
 
 /// Resolution options for the `null_byte` condition
 pub enum NullByteResolution {
@@ -241,24 +242,22 @@ impl<'self> ToCStr for &'self str {
     unsafe fn to_c_str_unchecked(&self) -> CString {
         self.as_bytes().to_c_str_unchecked()
     }
+
+    #[inline]
+    fn with_c_str<T>(&self, f: &fn(*libc::c_char) -> T) -> T {
+        self.as_bytes().with_c_str(f)
+    }
 }
 
+// The length of the stack allocated buffer for `vec.with_c_str()`
+static BUF_LEN: uint = 32;
+
 impl<'self> ToCStr for &'self [u8] {
     fn to_c_str(&self) -> CString {
         #[fixed_stack_segment]; #[inline(never)];
         let mut cs = unsafe { self.to_c_str_unchecked() };
         do cs.with_mut_ref |buf| {
-            for i in range(0, self.len()) {
-                unsafe {
-                    let p = buf.offset(i as int);
-                    if *p == 0 {
-                        match null_byte::cond.raise(self.to_owned()) {
-                            Truncate => break,
-                            ReplaceWith(c) => *p = c
-                        }
-                    }
-                }
-            }
+            check_for_null(*self, buf);
         }
         cs
     }
@@ -277,6 +276,45 @@ impl<'self> ToCStr for &'self [u8] {
             CString::new(buf as *libc::c_char, true)
         }
     }
+
+    /// WARNING: This function uses an optimization to only malloc a temporary
+    /// CString when the source string is small. Do not save a reference to
+    /// the `*libc::c_char` as it may be invalid after this function call.
+    fn with_c_str<T>(&self, f: &fn(*libc::c_char) -> T) -> T {
+        if self.len() < BUF_LEN {
+            do self.as_imm_buf |self_buf, self_len| {
+                unsafe {
+                    let mut buf: [u8, .. BUF_LEN] = intrinsics::uninit();
+
+                    do buf.as_mut_buf |buf, _| {
+                        ptr::copy_memory(buf, self_buf, self_len);
+                        *ptr::mut_offset(buf, self_len as int) = 0;
+
+                        check_for_null(*self, buf as *mut libc::c_char);
+
+                        f(buf as *libc::c_char)
+                    }
+                }
+            }
+        } else {
+            self.to_c_str().with_ref(f)
+        }
+    }
+}
+
+#[inline]
+fn check_for_null(v: &[u8], buf: *mut libc::c_char) {
+    for i in range(0, v.len()) {
+        unsafe {
+            let p = buf.offset(i as int);
+            if *p == 0 {
+                match null_byte::cond.raise(v.to_owned()) {
+                    Truncate => break,
+                    ReplaceWith(c) => *p = c
+                }
+            }
+        }
+    }
 }
 
 /// External iterator for a CString's bytes.