]> git.lizzy.rs Git - rust.git/blob - src/libstd/c_str.rs
a7234eeb1d5fbaa83fe5ba413993454777c58263
[rust.git] / src / libstd / c_str.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*!
12
13 C-string manipulation and management
14
15 This modules provides the basic methods for creating and manipulating
16 null-terminated strings for use with FFI calls (back to C). Most C APIs require
17 that the string being passed to them is null-terminated, and by default rust's
18 string types are *not* null terminated.
19
20 The other problem with translating Rust strings to C strings is that Rust
21 strings can validly contain a null-byte in the middle of the string (0 is a
22 valid unicode codepoint). This means that not all Rust strings can actually be
23 translated to C strings.
24
25 # Creation of a C string
26
27 A C string is managed through the `CString` type defined in this module. It
28 "owns" the internal buffer of characters and will automatically deallocate the
29 buffer when the string is dropped. The `ToCStr` trait is implemented for `&str`
30 and `&[u8]`, but the conversions can fail due to some of the limitations
31 explained above.
32
33 This also means that currently whenever a C string is created, an allocation
34 must be performed to place the data elsewhere (the lifetime of the C string is
35 not tied to the lifetime of the original string/data buffer). If C strings are
36 heavily used in applications, then caching may be advisable to prevent
37 unnecessary amounts of allocations.
38
39 An example of creating and using a C string would be:
40
41 ```rust
42 extern crate libc;
43
44 extern {
45     fn puts(s: *libc::c_char);
46 }
47
48 fn main() {
49     let my_string = "Hello, world!";
50
51     // Allocate the C string with an explicit local that owns the string. The
52     // `c_buffer` pointer will be deallocated when `my_c_string` goes out of scope.
53     let my_c_string = my_string.to_c_str();
54     my_c_string.with_ref(|c_buffer| {
55         unsafe { puts(c_buffer); }
56     });
57
58     // Don't save off the allocation of the C string, the `c_buffer` will be
59     // deallocated when this block returns!
60     my_string.with_c_str(|c_buffer| {
61         unsafe { puts(c_buffer); }
62     });
63 }
64  ```
65
66 */
67
68 use cast;
69 use container::Container;
70 use iter::{Iterator, range};
71 use libc;
72 use kinds::marker;
73 use ops::Drop;
74 use cmp::Eq;
75 use clone::Clone;
76 use mem;
77 use option::{Option, Some, None};
78 use ptr::RawPtr;
79 use ptr;
80 use str::StrSlice;
81 use str;
82 use slice::{ImmutableVector, MutableVector};
83 use slice;
84 use rt::global_heap::malloc_raw;
85 use raw::Slice;
86
87 /// The representation of a C String.
88 ///
89 /// This structure wraps a `*libc::c_char`, and will automatically free the
90 /// memory it is pointing to when it goes out of scope.
91 pub struct CString {
92     buf: *libc::c_char,
93     owns_buffer_: bool,
94 }
95
96 impl Clone for CString {
97     /// Clone this CString into a new, uniquely owned CString. For safety
98     /// reasons, this is always a deep clone, rather than the usual shallow
99     /// clone.
100     fn clone(&self) -> CString {
101         if self.buf.is_null() {
102             CString { buf: self.buf, owns_buffer_: self.owns_buffer_ }
103         } else {
104             let len = self.len() + 1;
105             let buf = unsafe { malloc_raw(len) } as *mut libc::c_char;
106             unsafe { ptr::copy_nonoverlapping_memory(buf, self.buf, len); }
107             CString { buf: buf as *libc::c_char, owns_buffer_: true }
108         }
109     }
110 }
111
112 impl Eq for CString {
113     fn eq(&self, other: &CString) -> bool {
114         if self.buf as uint == other.buf as uint {
115             true
116         } else if self.buf.is_null() || other.buf.is_null() {
117             false
118         } else {
119             unsafe {
120                 libc::strcmp(self.buf, other.buf) == 0
121             }
122         }
123     }
124 }
125
126 impl CString {
127     /// Create a C String from a pointer.
128     pub unsafe fn new(buf: *libc::c_char, owns_buffer: bool) -> CString {
129         CString { buf: buf, owns_buffer_: owns_buffer }
130     }
131
132     /// Unwraps the wrapped `*libc::c_char` from the `CString` wrapper.
133     /// Any ownership of the buffer by the `CString` wrapper is forgotten.
134     pub unsafe fn unwrap(self) -> *libc::c_char {
135         let mut c_str = self;
136         c_str.owns_buffer_ = false;
137         c_str.buf
138     }
139
140     /// Calls a closure with a reference to the underlying `*libc::c_char`.
141     ///
142     /// # Failure
143     ///
144     /// Fails if the CString is null.
145     pub fn with_ref<T>(&self, f: |*libc::c_char| -> T) -> T {
146         if self.buf.is_null() { fail!("CString is null!"); }
147         f(self.buf)
148     }
149
150     /// Calls a closure with a mutable reference to the underlying `*libc::c_char`.
151     ///
152     /// # Failure
153     ///
154     /// Fails if the CString is null.
155     pub fn with_mut_ref<T>(&mut self, f: |*mut libc::c_char| -> T) -> T {
156         if self.buf.is_null() { fail!("CString is null!"); }
157         f(unsafe { cast::transmute_mut_unsafe(self.buf) })
158     }
159
160     /// Returns true if the CString is a null.
161     pub fn is_null(&self) -> bool {
162         self.buf.is_null()
163     }
164
165     /// Returns true if the CString is not null.
166     pub fn is_not_null(&self) -> bool {
167         self.buf.is_not_null()
168     }
169
170     /// Returns whether or not the `CString` owns the buffer.
171     pub fn owns_buffer(&self) -> bool {
172         self.owns_buffer_
173     }
174
175     /// Converts the CString into a `&[u8]` without copying.
176     /// Includes the terminating NUL byte.
177     ///
178     /// # Failure
179     ///
180     /// Fails if the CString is null.
181     #[inline]
182     pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
183         if self.buf.is_null() { fail!("CString is null!"); }
184         unsafe {
185             cast::transmute(Slice { data: self.buf, len: self.len() + 1 })
186         }
187     }
188
189     /// Converts the CString into a `&[u8]` without copying.
190     /// Does not include the terminating NUL byte.
191     ///
192     /// # Failure
193     ///
194     /// Fails if the CString is null.
195     #[inline]
196     pub fn as_bytes_no_nul<'a>(&'a self) -> &'a [u8] {
197         if self.buf.is_null() { fail!("CString is null!"); }
198         unsafe {
199             cast::transmute(Slice { data: self.buf, len: self.len() })
200         }
201     }
202
203     /// Converts the CString into a `&str` without copying.
204     /// Returns None if the CString is not UTF-8.
205     ///
206     /// # Failure
207     ///
208     /// Fails if the CString is null.
209     #[inline]
210     pub fn as_str<'a>(&'a self) -> Option<&'a str> {
211         let buf = self.as_bytes_no_nul();
212         str::from_utf8(buf)
213     }
214
215     /// Return a CString iterator.
216     ///
217     /// # Failure
218     ///
219     /// Fails if the CString is null.
220     pub fn iter<'a>(&'a self) -> CChars<'a> {
221         if self.buf.is_null() { fail!("CString is null!"); }
222         CChars {
223             ptr: self.buf,
224             marker: marker::ContravariantLifetime,
225         }
226     }
227 }
228
229 impl Drop for CString {
230     fn drop(&mut self) {
231         if self.owns_buffer_ {
232             unsafe {
233                 libc::free(self.buf as *mut libc::c_void)
234             }
235         }
236     }
237 }
238
239 impl Container for CString {
240     /// Return the number of bytes in the CString (not including the NUL terminator).
241     ///
242     /// # Failure
243     ///
244     /// Fails if the CString is null.
245     #[inline]
246     fn len(&self) -> uint {
247         if self.buf.is_null() { fail!("CString is null!"); }
248         unsafe {
249             ptr::position(self.buf, |c| *c == 0)
250         }
251     }
252 }
253
254 /// A generic trait for converting a value to a CString.
255 pub trait ToCStr {
256     /// Copy the receiver into a CString.
257     ///
258     /// # Failure
259     ///
260     /// Fails the task if the receiver has an interior null.
261     fn to_c_str(&self) -> CString;
262
263     /// Unsafe variant of `to_c_str()` that doesn't check for nulls.
264     unsafe fn to_c_str_unchecked(&self) -> CString;
265
266     /// Work with a temporary CString constructed from the receiver.
267     /// The provided `*libc::c_char` will be freed immediately upon return.
268     ///
269     /// # Example
270     ///
271     /// ```rust
272     /// extern crate libc;
273     ///
274     /// fn main() {
275     ///     let s = "PATH".with_c_str(|path| unsafe {
276     ///         libc::getenv(path)
277     ///     });
278     /// }
279     /// ```
280     ///
281     /// # Failure
282     ///
283     /// Fails the task if the receiver has an interior null.
284     #[inline]
285     fn with_c_str<T>(&self, f: |*libc::c_char| -> T) -> T {
286         self.to_c_str().with_ref(f)
287     }
288
289     /// Unsafe variant of `with_c_str()` that doesn't check for nulls.
290     #[inline]
291     unsafe fn with_c_str_unchecked<T>(&self, f: |*libc::c_char| -> T) -> T {
292         self.to_c_str_unchecked().with_ref(f)
293     }
294 }
295
296 impl<'a> ToCStr for &'a str {
297     #[inline]
298     fn to_c_str(&self) -> CString {
299         self.as_bytes().to_c_str()
300     }
301
302     #[inline]
303     unsafe fn to_c_str_unchecked(&self) -> CString {
304         self.as_bytes().to_c_str_unchecked()
305     }
306
307     #[inline]
308     fn with_c_str<T>(&self, f: |*libc::c_char| -> T) -> T {
309         self.as_bytes().with_c_str(f)
310     }
311
312     #[inline]
313     unsafe fn with_c_str_unchecked<T>(&self, f: |*libc::c_char| -> T) -> T {
314         self.as_bytes().with_c_str_unchecked(f)
315     }
316 }
317
318 // The length of the stack allocated buffer for `vec.with_c_str()`
319 static BUF_LEN: uint = 128;
320
321 impl<'a> ToCStr for &'a [u8] {
322     fn to_c_str(&self) -> CString {
323         let mut cs = unsafe { self.to_c_str_unchecked() };
324         cs.with_mut_ref(|buf| check_for_null(*self, buf));
325         cs
326     }
327
328     unsafe fn to_c_str_unchecked(&self) -> CString {
329         let self_len = self.len();
330         let buf = malloc_raw(self_len + 1);
331
332         ptr::copy_memory(buf, self.as_ptr(), self_len);
333         *buf.offset(self_len as int) = 0;
334
335         CString::new(buf as *libc::c_char, true)
336     }
337
338     fn with_c_str<T>(&self, f: |*libc::c_char| -> T) -> T {
339         unsafe { with_c_str(*self, true, f) }
340     }
341
342     unsafe fn with_c_str_unchecked<T>(&self, f: |*libc::c_char| -> T) -> T {
343         with_c_str(*self, false, f)
344     }
345 }
346
347 // Unsafe function that handles possibly copying the &[u8] into a stack array.
348 unsafe fn with_c_str<T>(v: &[u8], checked: bool, f: |*libc::c_char| -> T) -> T {
349     if v.len() < BUF_LEN {
350         let mut buf: [u8, .. BUF_LEN] = mem::uninit();
351         slice::bytes::copy_memory(buf, v);
352         buf[v.len()] = 0;
353
354         let buf = buf.as_mut_ptr();
355         if checked {
356             check_for_null(v, buf as *mut libc::c_char);
357         }
358
359         f(buf as *libc::c_char)
360     } else if checked {
361         v.to_c_str().with_ref(f)
362     } else {
363         v.to_c_str_unchecked().with_ref(f)
364     }
365 }
366
367 #[inline]
368 fn check_for_null(v: &[u8], buf: *mut libc::c_char) {
369     for i in range(0, v.len()) {
370         unsafe {
371             let p = buf.offset(i as int);
372             assert!(*p != 0);
373         }
374     }
375 }
376
377 /// External iterator for a CString's bytes.
378 ///
379 /// Use with the `std::iter` module.
380 pub struct CChars<'a> {
381     ptr: *libc::c_char,
382     marker: marker::ContravariantLifetime<'a>,
383 }
384
385 impl<'a> Iterator<libc::c_char> for CChars<'a> {
386     fn next(&mut self) -> Option<libc::c_char> {
387         let ch = unsafe { *self.ptr };
388         if ch == 0 {
389             None
390         } else {
391             self.ptr = unsafe { self.ptr.offset(1) };
392             Some(ch)
393         }
394     }
395 }
396
397 /// Parses a C "multistring", eg windows env values or
398 /// the req->ptr result in a uv_fs_readdir() call.
399 ///
400 /// Optionally, a `count` can be passed in, limiting the
401 /// parsing to only being done `count`-times.
402 ///
403 /// The specified closure is invoked with each string that
404 /// is found, and the number of strings found is returned.
405 pub unsafe fn from_c_multistring(buf: *libc::c_char,
406                                  count: Option<uint>,
407                                  f: |&CString|) -> uint {
408
409     let mut curr_ptr: uint = buf as uint;
410     let mut ctr = 0;
411     let (limited_count, limit) = match count {
412         Some(limit) => (true, limit),
413         None => (false, 0)
414     };
415     while ((limited_count && ctr < limit) || !limited_count)
416           && *(curr_ptr as *libc::c_char) != 0 as libc::c_char {
417         let cstr = CString::new(curr_ptr as *libc::c_char, false);
418         f(&cstr);
419         curr_ptr += cstr.len() + 1;
420         ctr += 1;
421     }
422     return ctr;
423 }
424
425 #[cfg(test)]
426 mod tests {
427     use prelude::*;
428     use super::*;
429     use libc;
430     use ptr;
431
432     #[test]
433     fn test_str_multistring_parsing() {
434         unsafe {
435             let input = bytes!("zero", "\x00", "one", "\x00", "\x00");
436             let ptr = input.as_ptr();
437             let expected = ["zero", "one"];
438             let mut it = expected.iter();
439             let result = from_c_multistring(ptr as *libc::c_char, None, |c| {
440                 let cbytes = c.as_bytes_no_nul();
441                 assert_eq!(cbytes, it.next().unwrap().as_bytes());
442             });
443             assert_eq!(result, 2);
444             assert!(it.next().is_none());
445         }
446     }
447
448     #[test]
449     fn test_str_to_c_str() {
450         "".to_c_str().with_ref(|buf| {
451             unsafe {
452                 assert_eq!(*buf.offset(0), 0);
453             }
454         });
455
456         "hello".to_c_str().with_ref(|buf| {
457             unsafe {
458                 assert_eq!(*buf.offset(0), 'h' as libc::c_char);
459                 assert_eq!(*buf.offset(1), 'e' as libc::c_char);
460                 assert_eq!(*buf.offset(2), 'l' as libc::c_char);
461                 assert_eq!(*buf.offset(3), 'l' as libc::c_char);
462                 assert_eq!(*buf.offset(4), 'o' as libc::c_char);
463                 assert_eq!(*buf.offset(5), 0);
464             }
465         })
466     }
467
468     #[test]
469     fn test_vec_to_c_str() {
470         let b: &[u8] = [];
471         b.to_c_str().with_ref(|buf| {
472             unsafe {
473                 assert_eq!(*buf.offset(0), 0);
474             }
475         });
476
477         let _ = bytes!("hello").to_c_str().with_ref(|buf| {
478             unsafe {
479                 assert_eq!(*buf.offset(0), 'h' as libc::c_char);
480                 assert_eq!(*buf.offset(1), 'e' as libc::c_char);
481                 assert_eq!(*buf.offset(2), 'l' as libc::c_char);
482                 assert_eq!(*buf.offset(3), 'l' as libc::c_char);
483                 assert_eq!(*buf.offset(4), 'o' as libc::c_char);
484                 assert_eq!(*buf.offset(5), 0);
485             }
486         });
487
488         let _ = bytes!("foo", 0xff).to_c_str().with_ref(|buf| {
489             unsafe {
490                 assert_eq!(*buf.offset(0), 'f' as libc::c_char);
491                 assert_eq!(*buf.offset(1), 'o' as libc::c_char);
492                 assert_eq!(*buf.offset(2), 'o' as libc::c_char);
493                 assert_eq!(*buf.offset(3), 0xff as i8);
494                 assert_eq!(*buf.offset(4), 0);
495             }
496         });
497     }
498
499     #[test]
500     fn test_is_null() {
501         let c_str = unsafe { CString::new(ptr::null(), false) };
502         assert!(c_str.is_null());
503         assert!(!c_str.is_not_null());
504     }
505
506     #[test]
507     fn test_unwrap() {
508         let c_str = "hello".to_c_str();
509         unsafe { libc::free(c_str.unwrap() as *mut libc::c_void) }
510     }
511
512     #[test]
513     fn test_with_ref() {
514         let c_str = "hello".to_c_str();
515         let len = unsafe { c_str.with_ref(|buf| libc::strlen(buf)) };
516         assert!(!c_str.is_null());
517         assert!(c_str.is_not_null());
518         assert_eq!(len, 5);
519     }
520
521     #[test]
522     #[should_fail]
523     fn test_with_ref_empty_fail() {
524         let c_str = unsafe { CString::new(ptr::null(), false) };
525         c_str.with_ref(|_| ());
526     }
527
528     #[test]
529     fn test_iterator() {
530         let c_str = "".to_c_str();
531         let mut iter = c_str.iter();
532         assert_eq!(iter.next(), None);
533
534         let c_str = "hello".to_c_str();
535         let mut iter = c_str.iter();
536         assert_eq!(iter.next(), Some('h' as libc::c_char));
537         assert_eq!(iter.next(), Some('e' as libc::c_char));
538         assert_eq!(iter.next(), Some('l' as libc::c_char));
539         assert_eq!(iter.next(), Some('l' as libc::c_char));
540         assert_eq!(iter.next(), Some('o' as libc::c_char));
541         assert_eq!(iter.next(), None);
542     }
543
544     #[test]
545     fn test_to_c_str_fail() {
546         use task;
547         assert!(task::try(proc() { "he\x00llo".to_c_str() }).is_err());
548     }
549
550     #[test]
551     fn test_to_c_str_unchecked() {
552         unsafe {
553             "he\x00llo".to_c_str_unchecked().with_ref(|buf| {
554                 assert_eq!(*buf.offset(0), 'h' as libc::c_char);
555                 assert_eq!(*buf.offset(1), 'e' as libc::c_char);
556                 assert_eq!(*buf.offset(2), 0);
557                 assert_eq!(*buf.offset(3), 'l' as libc::c_char);
558                 assert_eq!(*buf.offset(4), 'l' as libc::c_char);
559                 assert_eq!(*buf.offset(5), 'o' as libc::c_char);
560                 assert_eq!(*buf.offset(6), 0);
561             })
562         }
563     }
564
565     #[test]
566     fn test_as_bytes() {
567         let c_str = "hello".to_c_str();
568         assert_eq!(c_str.as_bytes(), bytes!("hello", 0));
569         let c_str = "".to_c_str();
570         assert_eq!(c_str.as_bytes(), bytes!(0));
571         let c_str = bytes!("foo", 0xff).to_c_str();
572         assert_eq!(c_str.as_bytes(), bytes!("foo", 0xff, 0));
573     }
574
575     #[test]
576     fn test_as_bytes_no_nul() {
577         let c_str = "hello".to_c_str();
578         assert_eq!(c_str.as_bytes_no_nul(), bytes!("hello"));
579         let c_str = "".to_c_str();
580         let exp: &[u8] = [];
581         assert_eq!(c_str.as_bytes_no_nul(), exp);
582         let c_str = bytes!("foo", 0xff).to_c_str();
583         assert_eq!(c_str.as_bytes_no_nul(), bytes!("foo", 0xff));
584     }
585
586     #[test]
587     #[should_fail]
588     fn test_as_bytes_fail() {
589         let c_str = unsafe { CString::new(ptr::null(), false) };
590         c_str.as_bytes();
591     }
592
593     #[test]
594     #[should_fail]
595     fn test_as_bytes_no_nul_fail() {
596         let c_str = unsafe { CString::new(ptr::null(), false) };
597         c_str.as_bytes_no_nul();
598     }
599
600     #[test]
601     fn test_as_str() {
602         let c_str = "hello".to_c_str();
603         assert_eq!(c_str.as_str(), Some("hello"));
604         let c_str = "".to_c_str();
605         assert_eq!(c_str.as_str(), Some(""));
606         let c_str = bytes!("foo", 0xff).to_c_str();
607         assert_eq!(c_str.as_str(), None);
608     }
609
610     #[test]
611     #[should_fail]
612     fn test_as_str_fail() {
613         let c_str = unsafe { CString::new(ptr::null(), false) };
614         c_str.as_str();
615     }
616
617     #[test]
618     #[should_fail]
619     fn test_len_fail() {
620         let c_str = unsafe { CString::new(ptr::null(), false) };
621         c_str.len();
622     }
623
624     #[test]
625     #[should_fail]
626     fn test_iter_fail() {
627         let c_str = unsafe { CString::new(ptr::null(), false) };
628         c_str.iter();
629     }
630
631     #[test]
632     fn test_clone() {
633         let a = "hello".to_c_str();
634         let b = a.clone();
635         assert!(a == b);
636     }
637
638     #[test]
639     fn test_clone_noleak() {
640         fn foo(f: |c: &CString|) {
641             let s = ~"test";
642             let c = s.to_c_str();
643             // give the closure a non-owned CString
644             let mut c_ = c.with_ref(|c| unsafe { CString::new(c, false) } );
645             f(&c_);
646             // muck with the buffer for later printing
647             c_.with_mut_ref(|c| unsafe { *c = 'X' as libc::c_char } );
648         }
649
650         let mut c_: Option<CString> = None;
651         foo(|c| {
652             c_ = Some(c.clone());
653             c.clone();
654             // force a copy, reading the memory
655             c.as_bytes().to_owned();
656         });
657         let c_ = c_.unwrap();
658         // force a copy, reading the memory
659         c_.as_bytes().to_owned();
660     }
661
662     #[test]
663     fn test_clone_eq_null() {
664         let x = unsafe { CString::new(ptr::null(), false) };
665         let y = x.clone();
666         assert!(x == y);
667     }
668 }
669
670 #[cfg(test)]
671 mod bench {
672     extern crate test;
673     use self::test::Bencher;
674     use libc;
675     use prelude::*;
676
677     #[inline]
678     fn check(s: &str, c_str: *libc::c_char) {
679         let s_buf = s.as_ptr();
680         for i in range(0, s.len()) {
681             unsafe {
682                 assert_eq!(
683                     *s_buf.offset(i as int) as libc::c_char,
684                     *c_str.offset(i as int));
685             }
686         }
687     }
688
689     static s_short: &'static str = "Mary";
690     static s_medium: &'static str = "Mary had a little lamb";
691     static s_long: &'static str = "\
692         Mary had a little lamb, Little lamb
693         Mary had a little lamb, Little lamb
694         Mary had a little lamb, Little lamb
695         Mary had a little lamb, Little lamb
696         Mary had a little lamb, Little lamb
697         Mary had a little lamb, Little lamb";
698
699     fn bench_to_str(b: &mut Bencher, s: &str) {
700         b.iter(|| {
701             let c_str = s.to_c_str();
702             c_str.with_ref(|c_str_buf| check(s, c_str_buf))
703         })
704     }
705
706     #[bench]
707     fn bench_to_c_str_short(b: &mut Bencher) {
708         bench_to_str(b, s_short)
709     }
710
711     #[bench]
712     fn bench_to_c_str_medium(b: &mut Bencher) {
713         bench_to_str(b, s_medium)
714     }
715
716     #[bench]
717     fn bench_to_c_str_long(b: &mut Bencher) {
718         bench_to_str(b, s_long)
719     }
720
721     fn bench_to_c_str_unchecked(b: &mut Bencher, s: &str) {
722         b.iter(|| {
723             let c_str = unsafe { s.to_c_str_unchecked() };
724             c_str.with_ref(|c_str_buf| check(s, c_str_buf))
725         })
726     }
727
728     #[bench]
729     fn bench_to_c_str_unchecked_short(b: &mut Bencher) {
730         bench_to_c_str_unchecked(b, s_short)
731     }
732
733     #[bench]
734     fn bench_to_c_str_unchecked_medium(b: &mut Bencher) {
735         bench_to_c_str_unchecked(b, s_medium)
736     }
737
738     #[bench]
739     fn bench_to_c_str_unchecked_long(b: &mut Bencher) {
740         bench_to_c_str_unchecked(b, s_long)
741     }
742
743     fn bench_with_c_str(b: &mut Bencher, s: &str) {
744         b.iter(|| {
745             s.with_c_str(|c_str_buf| check(s, c_str_buf))
746         })
747     }
748
749     #[bench]
750     fn bench_with_c_str_short(b: &mut Bencher) {
751         bench_with_c_str(b, s_short)
752     }
753
754     #[bench]
755     fn bench_with_c_str_medium(b: &mut Bencher) {
756         bench_with_c_str(b, s_medium)
757     }
758
759     #[bench]
760     fn bench_with_c_str_long(b: &mut Bencher) {
761         bench_with_c_str(b, s_long)
762     }
763
764     fn bench_with_c_str_unchecked(b: &mut Bencher, s: &str) {
765         b.iter(|| {
766             unsafe {
767                 s.with_c_str_unchecked(|c_str_buf| check(s, c_str_buf))
768             }
769         })
770     }
771
772     #[bench]
773     fn bench_with_c_str_unchecked_short(b: &mut Bencher) {
774         bench_with_c_str_unchecked(b, s_short)
775     }
776
777     #[bench]
778     fn bench_with_c_str_unchecked_medium(b: &mut Bencher) {
779         bench_with_c_str_unchecked(b, s_medium)
780     }
781
782     #[bench]
783     fn bench_with_c_str_unchecked_long(b: &mut Bencher) {
784         bench_with_c_str_unchecked(b, s_long)
785     }
786 }