@@ -61,16 +61,18 @@ do my_string.with_c_str |c_buffer| {
61
61
*/
62
62
63
63
use cast;
64
+ use container:: Container ;
64
65
use iter:: { Iterator , range} ;
65
66
use libc;
66
67
use ops:: Drop ;
67
68
use option:: { Option , Some , None } ;
68
69
use ptr:: RawPtr ;
69
70
use ptr;
70
- use str;
71
71
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;
74
76
75
77
/// Resolution options for the `null_byte` condition
76
78
pub enum NullByteResolution {
@@ -152,8 +154,7 @@ impl CString {
152
154
pub fn as_bytes < ' a > ( & ' a self ) -> & ' a [ u8 ] {
153
155
if self . buf . is_null ( ) { fail ! ( "CString is null!" ) ; }
154
156
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 ) )
157
158
}
158
159
}
159
160
@@ -187,6 +188,15 @@ impl Drop for CString {
187
188
}
188
189
}
189
190
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
+
190
200
/// A generic trait for converting a value to a CString.
191
201
pub trait ToCStr {
192
202
/// Copy the receiver into a CString.
@@ -233,24 +243,27 @@ impl<'self> ToCStr for &'self str {
233
243
unsafe fn to_c_str_unchecked ( & self ) -> CString {
234
244
self . as_bytes ( ) . to_c_str_unchecked ( )
235
245
}
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
+ }
236
256
}
237
257
258
+ // The length of the stack allocated buffer for `vec.with_c_str()`
259
+ static BUF_LEN : uint = 128 ;
260
+
238
261
impl < ' self > ToCStr for & ' self [ u8 ] {
239
262
fn to_c_str ( & self ) -> CString {
240
263
#[ fixed_stack_segment] ; #[ inline( never) ] ;
241
264
let mut cs = unsafe { self . to_c_str_unchecked ( ) } ;
242
265
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) ;
254
267
}
255
268
cs
256
269
}
@@ -269,6 +282,50 @@ impl<'self> ToCStr for &'self [u8] {
269
282
CString :: new ( buf as * libc:: c_char , true )
270
283
}
271
284
}
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
+ }
272
329
}
273
330
274
331
/// External iterator for a CString's bytes.
@@ -471,3 +528,131 @@ mod tests {
471
528
assert_eq ! ( c_str. as_str( ) , None ) ;
472
529
}
473
530
}
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
+ }
0 commit comments