Skip to content

Commit d6774f8

Browse files
committed
auto merge of #9352 : erickt/rust/master, r=huonw
One of the downsides with `c_str` is that it always forces an allocation, and so this could add unnecessary overhead to various calls. This PR implements one of the suggestions @graydon made in #8296 for `vec.with_c_str` in that for a short string can use a small stack array instead of a malloced array for our temporary c string. This ends up being twice as fast for small strings. There are two things to consider before landing this commit though. First off, I arbitrarily picked the stack array as 32 bytes, and I'm not sure if this a reasonable amount or not. Second, there is a risk that someone can keep a reference to the interior stack pointer, which could cause mayhem if someone were to dereference the pointer. Since we also can easily grab and return interior pointers to vecs though, I don't think this is that much of an issue.
2 parents eb3ebb7 + b1ee87f commit d6774f8

File tree

8 files changed

+229
-45
lines changed

8 files changed

+229
-45
lines changed

src/librustc/middle/trans/base.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -524,13 +524,13 @@ pub fn set_always_inline(f: ValueRef) {
524524
}
525525

526526
pub fn set_fixed_stack_segment(f: ValueRef) {
527-
do "fixed-stack-segment".to_c_str().with_ref |buf| {
527+
do "fixed-stack-segment".with_c_str |buf| {
528528
unsafe { llvm::LLVMAddFunctionAttrString(f, buf); }
529529
}
530530
}
531531

532532
pub fn set_no_split_stack(f: ValueRef) {
533-
do "no-split-stack".to_c_str().with_ref |buf| {
533+
do "no-split-stack".with_c_str |buf| {
534534
unsafe { llvm::LLVMAddFunctionAttrString(f, buf); }
535535
}
536536
}

src/librustc/middle/trans/debuginfo.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
781781

782782
let ident = special_idents::type_self;
783783

784-
let param_metadata = do token::ident_to_str(&ident).to_c_str().with_ref |name| {
784+
let param_metadata = do token::ident_to_str(&ident).with_c_str |name| {
785785
unsafe {
786786
llvm::LLVMDIBuilderCreateTemplateTypeParameter(
787787
DIB(cx),
@@ -819,7 +819,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
819819
// Again, only create type information if extra_debuginfo is enabled
820820
if cx.sess.opts.extra_debuginfo {
821821
let actual_type_metadata = type_metadata(cx, actual_type, codemap::dummy_sp());
822-
let param_metadata = do token::ident_to_str(&ident).to_c_str().with_ref |name| {
822+
let param_metadata = do token::ident_to_str(&ident).with_c_str |name| {
823823
unsafe {
824824
llvm::LLVMDIBuilderCreateTemplateTypeParameter(
825825
DIB(cx),

src/librustc/middle/trans/foreign.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
465465
// }
466466

467467
let the_block =
468-
"the block".to_c_str().with_ref(
468+
"the block".with_c_str(
469469
|s| llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llwrapfn, s));
470470

471471
let builder = ccx.builder.B;
@@ -519,7 +519,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
519519

520520
None => {
521521
let slot = {
522-
"return_alloca".to_c_str().with_ref(
522+
"return_alloca".with_c_str(
523523
|s| llvm::LLVMBuildAlloca(builder,
524524
llrust_ret_ty.to_ref(),
525525
s))

src/libstd/c_str.rs

+201-16
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,18 @@ 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 vec;
75+
use unstable::intrinsics;
7476

7577
/// Resolution options for the `null_byte` condition
7678
pub enum NullByteResolution {
@@ -152,8 +154,7 @@ impl CString {
152154
pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
153155
if self.buf.is_null() { fail!("CString is null!"); }
154156
unsafe {
155-
let len = ptr::position(self.buf, |c| *c == 0);
156-
cast::transmute((self.buf, len + 1))
157+
cast::transmute((self.buf, self.len() + 1))
157158
}
158159
}
159160

@@ -187,6 +188,15 @@ impl Drop for CString {
187188
}
188189
}
189190

191+
impl Container for CString {
192+
#[inline]
193+
fn len(&self) -> uint {
194+
unsafe {
195+
ptr::position(self.buf, |c| *c == 0)
196+
}
197+
}
198+
}
199+
190200
/// A generic trait for converting a value to a CString.
191201
pub trait ToCStr {
192202
/// Copy the receiver into a CString.
@@ -233,24 +243,27 @@ impl<'self> ToCStr for &'self str {
233243
unsafe fn to_c_str_unchecked(&self) -> CString {
234244
self.as_bytes().to_c_str_unchecked()
235245
}
246+
247+
#[inline]
248+
fn with_c_str<T>(&self, f: &fn(*libc::c_char) -> T) -> T {
249+
self.as_bytes().with_c_str(f)
250+
}
251+
252+
#[inline]
253+
unsafe fn with_c_str_unchecked<T>(&self, f: &fn(*libc::c_char) -> T) -> T {
254+
self.as_bytes().with_c_str_unchecked(f)
255+
}
236256
}
237257

258+
// The length of the stack allocated buffer for `vec.with_c_str()`
259+
static BUF_LEN: uint = 128;
260+
238261
impl<'self> ToCStr for &'self [u8] {
239262
fn to_c_str(&self) -> CString {
240263
#[fixed_stack_segment]; #[inline(never)];
241264
let mut cs = unsafe { self.to_c_str_unchecked() };
242265
do cs.with_mut_ref |buf| {
243-
for i in range(0, self.len()) {
244-
unsafe {
245-
let p = buf.offset(i as int);
246-
if *p == 0 {
247-
match null_byte::cond.raise(self.to_owned()) {
248-
Truncate => break,
249-
ReplaceWith(c) => *p = c
250-
}
251-
}
252-
}
253-
}
266+
check_for_null(*self, buf);
254267
}
255268
cs
256269
}
@@ -269,6 +282,50 @@ impl<'self> ToCStr for &'self [u8] {
269282
CString::new(buf as *libc::c_char, true)
270283
}
271284
}
285+
286+
fn with_c_str<T>(&self, f: &fn(*libc::c_char) -> T) -> T {
287+
unsafe { with_c_str(*self, true, f) }
288+
}
289+
290+
unsafe fn with_c_str_unchecked<T>(&self, f: &fn(*libc::c_char) -> T) -> T {
291+
with_c_str(*self, false, f)
292+
}
293+
}
294+
295+
// Unsafe function that handles possibly copying the &[u8] into a stack array.
296+
unsafe fn with_c_str<T>(v: &[u8], checked: bool, f: &fn(*libc::c_char) -> T) -> T {
297+
if v.len() < BUF_LEN {
298+
let mut buf: [u8, .. BUF_LEN] = intrinsics::uninit();
299+
vec::bytes::copy_memory(buf, v, v.len());
300+
buf[v.len()] = 0;
301+
302+
do buf.as_mut_buf |buf, _| {
303+
if checked {
304+
check_for_null(v, buf as *mut libc::c_char);
305+
}
306+
307+
f(buf as *libc::c_char)
308+
}
309+
} else if checked {
310+
v.to_c_str().with_ref(f)
311+
} else {
312+
v.to_c_str_unchecked().with_ref(f)
313+
}
314+
}
315+
316+
#[inline]
317+
fn check_for_null(v: &[u8], buf: *mut libc::c_char) {
318+
for i in range(0, v.len()) {
319+
unsafe {
320+
let p = buf.offset(i as int);
321+
if *p == 0 {
322+
match null_byte::cond.raise(v.to_owned()) {
323+
Truncate => break,
324+
ReplaceWith(c) => *p = c
325+
}
326+
}
327+
}
328+
}
272329
}
273330

274331
/// External iterator for a CString's bytes.
@@ -471,3 +528,131 @@ mod tests {
471528
assert_eq!(c_str.as_str(), None);
472529
}
473530
}
531+
532+
#[cfg(test)]
533+
mod bench {
534+
use iter::range;
535+
use libc;
536+
use option::Some;
537+
use ptr;
538+
use extra::test::BenchHarness;
539+
540+
#[inline]
541+
fn check(s: &str, c_str: *libc::c_char) {
542+
do s.as_imm_buf |s_buf, s_len| {
543+
for i in range(0, s_len) {
544+
unsafe {
545+
assert_eq!(
546+
*ptr::offset(s_buf, i as int) as libc::c_char,
547+
*ptr::offset(c_str, i as int));
548+
}
549+
}
550+
}
551+
}
552+
553+
static s_short: &'static str = "Mary";
554+
static s_medium: &'static str = "Mary had a little lamb";
555+
static s_long: &'static str = "\
556+
Mary had a little lamb, Little lamb
557+
Mary had a little lamb, Little lamb
558+
Mary had a little lamb, Little lamb
559+
Mary had a little lamb, Little lamb
560+
Mary had a little lamb, Little lamb
561+
Mary had a little lamb, Little lamb";
562+
563+
fn bench_to_str(bh: &mut BenchHarness, s: &str) {
564+
do bh.iter {
565+
let c_str = s.to_c_str();
566+
do c_str.with_ref |c_str_buf| {
567+
check(s, c_str_buf)
568+
}
569+
}
570+
}
571+
572+
#[bench]
573+
fn bench_to_c_str_short(bh: &mut BenchHarness) {
574+
bench_to_str(bh, s_short)
575+
}
576+
577+
#[bench]
578+
fn bench_to_c_str_medium(bh: &mut BenchHarness) {
579+
bench_to_str(bh, s_medium)
580+
}
581+
582+
#[bench]
583+
fn bench_to_c_str_long(bh: &mut BenchHarness) {
584+
bench_to_str(bh, s_long)
585+
}
586+
587+
fn bench_to_c_str_unchecked(bh: &mut BenchHarness, s: &str) {
588+
do bh.iter {
589+
let c_str = unsafe { s.to_c_str_unchecked() };
590+
do c_str.with_ref |c_str_buf| {
591+
check(s, c_str_buf)
592+
}
593+
}
594+
}
595+
596+
#[bench]
597+
fn bench_to_c_str_unchecked_short(bh: &mut BenchHarness) {
598+
bench_to_c_str_unchecked(bh, s_short)
599+
}
600+
601+
#[bench]
602+
fn bench_to_c_str_unchecked_medium(bh: &mut BenchHarness) {
603+
bench_to_c_str_unchecked(bh, s_medium)
604+
}
605+
606+
#[bench]
607+
fn bench_to_c_str_unchecked_long(bh: &mut BenchHarness) {
608+
bench_to_c_str_unchecked(bh, s_long)
609+
}
610+
611+
fn bench_with_c_str(bh: &mut BenchHarness, s: &str) {
612+
do bh.iter {
613+
do s.with_c_str |c_str_buf| {
614+
check(s, c_str_buf)
615+
}
616+
}
617+
}
618+
619+
#[bench]
620+
fn bench_with_c_str_short(bh: &mut BenchHarness) {
621+
bench_with_c_str(bh, s_short)
622+
}
623+
624+
#[bench]
625+
fn bench_with_c_str_medium(bh: &mut BenchHarness) {
626+
bench_with_c_str(bh, s_medium)
627+
}
628+
629+
#[bench]
630+
fn bench_with_c_str_long(bh: &mut BenchHarness) {
631+
bench_with_c_str(bh, s_long)
632+
}
633+
634+
fn bench_with_c_str_unchecked(bh: &mut BenchHarness, s: &str) {
635+
do bh.iter {
636+
unsafe {
637+
do s.with_c_str_unchecked |c_str_buf| {
638+
check(s, c_str_buf)
639+
}
640+
}
641+
}
642+
}
643+
644+
#[bench]
645+
fn bench_with_c_str_unchecked_short(bh: &mut BenchHarness) {
646+
bench_with_c_str_unchecked(bh, s_short)
647+
}
648+
649+
#[bench]
650+
fn bench_with_c_str_unchecked_medium(bh: &mut BenchHarness) {
651+
bench_with_c_str_unchecked(bh, s_medium)
652+
}
653+
654+
#[bench]
655+
fn bench_with_c_str_unchecked_long(bh: &mut BenchHarness) {
656+
bench_with_c_str_unchecked(bh, s_long)
657+
}
658+
}

src/libstd/rt/crate_map.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ fn iter_crate_map_follow_children() {
209209
let child_crate1 = CrateMapT2 {
210210
version: 1,
211211
entries: vec::raw::to_ptr([
212-
ModEntry { name: "t::f1".to_c_str().with_ref(|buf| buf), log_level: &mut 1},
212+
ModEntry { name: "t::f1".with_c_str(|buf| buf), log_level: &mut 1},
213213
ModEntry { name: ptr::null(), log_level: ptr::mut_null()}
214214
]),
215215
children: [&child_crate2 as *CrateMap, ptr::null()]
@@ -219,7 +219,7 @@ fn iter_crate_map_follow_children() {
219219
let root_crate = CrateMapT2 {
220220
version: 1,
221221
entries: vec::raw::to_ptr([
222-
ModEntry { name: "t::f1".to_c_str().with_ref(|buf| buf), log_level: &mut 0},
222+
ModEntry { name: "t::f1".with_c_str(|buf| buf), log_level: &mut 0},
223223
ModEntry { name: ptr::null(), log_level: ptr::mut_null()}
224224
]),
225225
children: [child_crate1_ptr, ptr::null()]

src/libstd/rt/logging.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ fn update_entry_match_full_path() {
294294
LogDirective {name: Some(~"crate2"), level: 3}];
295295
let level = &mut 0;
296296
unsafe {
297-
do "crate1::mod1".to_c_str().with_ref |ptr| {
297+
do "crate1::mod1".with_c_str |ptr| {
298298
let entry= &ModEntry {name: ptr, log_level: level};
299299
let m = update_entry(dirs, transmute(entry));
300300
assert!(*entry.log_level == 2);
@@ -310,7 +310,7 @@ fn update_entry_no_match() {
310310
LogDirective {name: Some(~"crate2"), level: 3}];
311311
let level = &mut 0;
312312
unsafe {
313-
do "crate3::mod1".to_c_str().with_ref |ptr| {
313+
do "crate3::mod1".with_c_str |ptr| {
314314
let entry= &ModEntry {name: ptr, log_level: level};
315315
let m = update_entry(dirs, transmute(entry));
316316
assert!(*entry.log_level == DEFAULT_LOG_LEVEL);
@@ -326,7 +326,7 @@ fn update_entry_match_beginning() {
326326
LogDirective {name: Some(~"crate2"), level: 3}];
327327
let level = &mut 0;
328328
unsafe {
329-
do "crate2::mod1".to_c_str().with_ref |ptr| {
329+
do "crate2::mod1".with_c_str |ptr| {
330330
let entry= &ModEntry {name: ptr, log_level: level};
331331
let m = update_entry(dirs, transmute(entry));
332332
assert!(*entry.log_level == 3);
@@ -343,7 +343,7 @@ fn update_entry_match_beginning_longest_match() {
343343
LogDirective {name: Some(~"crate2::mod"), level: 4}];
344344
let level = &mut 0;
345345
unsafe {
346-
do "crate2::mod1".to_c_str().with_ref |ptr| {
346+
do "crate2::mod1".with_c_str |ptr| {
347347
let entry = &ModEntry {name: ptr, log_level: level};
348348
let m = update_entry(dirs, transmute(entry));
349349
assert!(*entry.log_level == 4);
@@ -360,13 +360,13 @@ fn update_entry_match_default() {
360360
];
361361
let level = &mut 0;
362362
unsafe {
363-
do "crate1::mod1".to_c_str().with_ref |ptr| {
363+
do "crate1::mod1".with_c_str |ptr| {
364364
let entry= &ModEntry {name: ptr, log_level: level};
365365
let m = update_entry(dirs, transmute(entry));
366366
assert!(*entry.log_level == 2);
367367
assert!(m == 1);
368368
}
369-
do "crate2::mod2".to_c_str().with_ref |ptr| {
369+
do "crate2::mod2".with_c_str |ptr| {
370370
let entry= &ModEntry {name: ptr, log_level: level};
371371
let m = update_entry(dirs, transmute(entry));
372372
assert!(*entry.log_level == 3);

0 commit comments

Comments
 (0)