Skip to content

Commit e02d1eb

Browse files
committed
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)
1 parent 410a96c commit e02d1eb

File tree

1 file changed

+52
-14
lines changed

1 file changed

+52
-14
lines changed

src/libstd/c_str.rs

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,17 @@ do my_string.with_c_str |c_buffer| {
6161
*/
6262

6363
use cast;
64+
use container::Container;
6465
use iter::{Iterator, range};
6566
use libc;
6667
use ops::Drop;
6768
use option::{Option, Some, None};
6869
use ptr::RawPtr;
6970
use ptr;
70-
use str;
7171
use str::StrSlice;
72-
use vec::{ImmutableVector, CopyableVector};
73-
use container::Container;
72+
use str;
73+
use vec::{CopyableVector, ImmutableVector, MutableVector};
74+
use unstable::intrinsics;
7475

7576
/// Resolution options for the `null_byte` condition
7677
pub enum NullByteResolution {
@@ -241,24 +242,22 @@ impl<'self> ToCStr for &'self str {
241242
unsafe fn to_c_str_unchecked(&self) -> CString {
242243
self.as_bytes().to_c_str_unchecked()
243244
}
245+
246+
#[inline]
247+
fn with_c_str<T>(&self, f: &fn(*libc::c_char) -> T) -> T {
248+
self.as_bytes().with_c_str(f)
249+
}
244250
}
245251

252+
// The length of the stack allocated buffer for `vec.with_c_str()`
253+
static BUF_LEN: uint = 32;
254+
246255
impl<'self> ToCStr for &'self [u8] {
247256
fn to_c_str(&self) -> CString {
248257
#[fixed_stack_segment]; #[inline(never)];
249258
let mut cs = unsafe { self.to_c_str_unchecked() };
250259
do cs.with_mut_ref |buf| {
251-
for i in range(0, self.len()) {
252-
unsafe {
253-
let p = buf.offset(i as int);
254-
if *p == 0 {
255-
match null_byte::cond.raise(self.to_owned()) {
256-
Truncate => break,
257-
ReplaceWith(c) => *p = c
258-
}
259-
}
260-
}
261-
}
260+
check_for_null(*self, buf);
262261
}
263262
cs
264263
}
@@ -277,6 +276,45 @@ impl<'self> ToCStr for &'self [u8] {
277276
CString::new(buf as *libc::c_char, true)
278277
}
279278
}
279+
280+
/// WARNING: This function uses an optimization to only malloc a temporary
281+
/// CString when the source string is small. Do not save a reference to
282+
/// the `*libc::c_char` as it may be invalid after this function call.
283+
fn with_c_str<T>(&self, f: &fn(*libc::c_char) -> T) -> T {
284+
if self.len() < BUF_LEN {
285+
do self.as_imm_buf |self_buf, self_len| {
286+
unsafe {
287+
let mut buf: [u8, .. BUF_LEN] = intrinsics::uninit();
288+
289+
do buf.as_mut_buf |buf, _| {
290+
ptr::copy_memory(buf, self_buf, self_len);
291+
*ptr::mut_offset(buf, self_len as int) = 0;
292+
293+
check_for_null(*self, buf as *mut libc::c_char);
294+
295+
f(buf as *libc::c_char)
296+
}
297+
}
298+
}
299+
} else {
300+
self.to_c_str().with_ref(f)
301+
}
302+
}
303+
}
304+
305+
#[inline]
306+
fn check_for_null(v: &[u8], buf: *mut libc::c_char) {
307+
for i in range(0, v.len()) {
308+
unsafe {
309+
let p = buf.offset(i as int);
310+
if *p == 0 {
311+
match null_byte::cond.raise(v.to_owned()) {
312+
Truncate => break,
313+
ReplaceWith(c) => *p = c
314+
}
315+
}
316+
}
317+
}
280318
}
281319

282320
/// External iterator for a CString's bytes.

0 commit comments

Comments
 (0)