]> git.lizzy.rs Git - rust.git/blob - src/librustrt/c_str.rs
68c2d2031c48dfa3fbbaa48f59c3cb4131eee27d
[rust.git] / src / librustrt / 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 Be carefull to remember that the memory is managed by C allocator API and not
40 by Rust allocator API.
41 That means that the CString pointers should be freed with C allocator API
42 if you intend to do that on your own, as the behaviour if you free them with
43 Rust's allocator API is not well defined
44
45 An example of creating and using a C string would be:
46
47 ```rust
48 extern crate libc;
49
50 extern {
51     fn puts(s: *const libc::c_char);
52 }
53
54 fn main() {
55     let my_string = "Hello, world!";
56
57     // Allocate the C string with an explicit local that owns the string. The
58     // `c_buffer` pointer will be deallocated when `my_c_string` goes out of scope.
59     let my_c_string = my_string.to_c_str();
60     unsafe {
61         puts(my_c_string.as_ptr());
62     }
63
64     // Don't save/return the pointer to the C string, the `c_buffer` will be
65     // deallocated when this block returns!
66     my_string.with_c_str(|c_buffer| {
67         unsafe { puts(c_buffer); }
68     });
69 }
70 ```
71
72 */
73
74 use collections::string::String;
75 use collections::hash;
76 use core::fmt;
77 use core::kinds::{Sized, marker};
78 use core::mem;
79 use core::prelude::{Clone, Drop, Eq, Iterator};
80 use core::prelude::{SlicePrelude, None, Option, Ordering, PartialEq};
81 use core::prelude::{PartialOrd, RawPtr, Some, StrPrelude, range};
82 use core::ptr;
83 use core::raw::Slice;
84 use core::slice;
85 use core::str;
86 use libc;
87
88 /// The representation of a C String.
89 ///
90 /// This structure wraps a `*libc::c_char`, and will automatically free the
91 /// memory it is pointing to when it goes out of scope.
92 pub struct CString {
93     buf: *const libc::c_char,
94     owns_buffer_: bool,
95 }
96
97 impl Clone for CString {
98     /// Clone this CString into a new, uniquely owned CString. For safety
99     /// reasons, this is always a deep clone with the memory allocated
100     /// with C's allocator API, rather than the usual shallow clone.
101     fn clone(&self) -> CString {
102         let len = self.len() + 1;
103         let buf = unsafe { libc::malloc(len as libc::size_t) } as *mut libc::c_char;
104         if buf.is_null() { ::alloc::oom() }
105         unsafe { ptr::copy_nonoverlapping_memory(buf, self.buf, len); }
106         CString { buf: buf as *const libc::c_char, owns_buffer_: true }
107     }
108 }
109
110 impl PartialEq for CString {
111     fn eq(&self, other: &CString) -> bool {
112         // Check if the two strings share the same buffer
113         if self.buf as uint == other.buf as uint {
114             true
115         } else {
116             unsafe {
117                 libc::strcmp(self.buf, other.buf) == 0
118             }
119         }
120     }
121 }
122
123 impl PartialOrd for CString {
124     // NOTE(stage0): remove method after a snapshot
125     #[cfg(stage0)]
126     #[inline]
127     fn partial_cmp(&self, other: &CString) -> Option<Ordering> {
128         self.as_bytes().partial_cmp(&other.as_bytes())
129     }
130     #[cfg(not(stage0))]  // NOTE(stage0): remove cfg after a snapshot
131     #[inline]
132     fn partial_cmp(&self, other: &CString) -> Option<Ordering> {
133         self.as_bytes().partial_cmp(other.as_bytes())
134     }
135 }
136
137 impl Eq for CString {}
138
139 impl<S: hash::Writer> hash::Hash<S> for CString {
140     #[inline]
141     fn hash(&self, state: &mut S) {
142         self.as_bytes().hash(state)
143     }
144 }
145
146 impl CString {
147     /// Create a C String from a pointer, with memory managed by C's allocator
148     /// API, so avoid calling it with a pointer to memory managed by Rust's
149     /// allocator API, as the behaviour would not be well defined.
150     ///
151     ///# Failure
152     ///
153     /// Fails if `buf` is null
154     pub unsafe fn new(buf: *const libc::c_char, owns_buffer: bool) -> CString {
155         assert!(!buf.is_null());
156         CString { buf: buf, owns_buffer_: owns_buffer }
157     }
158
159     /// Return a pointer to the NUL-terminated string data.
160     ///
161     /// `.as_ptr` returns an internal pointer into the `CString`, and
162     /// may be invalidated when the `CString` falls out of scope (the
163     /// destructor will run, freeing the allocation if there is
164     /// one).
165     ///
166     /// ```rust
167     /// let foo = "some string";
168     ///
169     /// // right
170     /// let x = foo.to_c_str();
171     /// let p = x.as_ptr();
172     ///
173     /// // wrong (the CString will be freed, invalidating `p`)
174     /// let p = foo.to_c_str().as_ptr();
175     /// ```
176     ///
177     /// # Example
178     ///
179     /// ```rust
180     /// extern crate libc;
181     ///
182     /// fn main() {
183     ///     let c_str = "foo bar".to_c_str();
184     ///     unsafe {
185     ///         libc::puts(c_str.as_ptr());
186     ///     }
187     /// }
188     /// ```
189     pub fn as_ptr(&self) -> *const libc::c_char {
190         self.buf
191     }
192
193     /// Return a mutable pointer to the NUL-terminated string data.
194     ///
195     /// `.as_mut_ptr` returns an internal pointer into the `CString`, and
196     /// may be invalidated when the `CString` falls out of scope (the
197     /// destructor will run, freeing the allocation if there is
198     /// one).
199     ///
200     /// ```rust
201     /// let foo = "some string";
202     ///
203     /// // right
204     /// let mut x = foo.to_c_str();
205     /// let p = x.as_mut_ptr();
206     ///
207     /// // wrong (the CString will be freed, invalidating `p`)
208     /// let p = foo.to_c_str().as_mut_ptr();
209     /// ```
210     pub fn as_mut_ptr(&mut self) -> *mut libc::c_char {
211         self.buf as *mut _
212     }
213
214     /// Returns whether or not the `CString` owns the buffer.
215     pub fn owns_buffer(&self) -> bool {
216         self.owns_buffer_
217     }
218
219     /// Converts the CString into a `&[u8]` without copying.
220     /// Includes the terminating NUL byte.
221     #[inline]
222     pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
223         unsafe {
224             mem::transmute(Slice { data: self.buf, len: self.len() + 1 })
225         }
226     }
227
228     /// Converts the CString into a `&[u8]` without copying.
229     /// Does not include the terminating NUL byte.
230     #[inline]
231     pub fn as_bytes_no_nul<'a>(&'a self) -> &'a [u8] {
232         unsafe {
233             mem::transmute(Slice { data: self.buf, len: self.len() })
234         }
235     }
236
237     /// Converts the CString into a `&str` without copying.
238     /// Returns None if the CString is not UTF-8.
239     #[inline]
240     pub fn as_str<'a>(&'a self) -> Option<&'a str> {
241         let buf = self.as_bytes_no_nul();
242         str::from_utf8(buf)
243     }
244
245     /// Return a CString iterator.
246     pub fn iter<'a>(&'a self) -> CChars<'a> {
247         CChars {
248             ptr: self.buf,
249             marker: marker::ContravariantLifetime,
250         }
251     }
252
253     /// Unwraps the wrapped `*libc::c_char` from the `CString` wrapper.
254     ///
255     /// Any ownership of the buffer by the `CString` wrapper is
256     /// forgotten, meaning that the backing allocation of this
257     /// `CString` is not automatically freed if it owns the
258     /// allocation. In this case, a user of `.unwrap()` should ensure
259     /// the allocation is freed, to avoid leaking memory. You should
260     /// use libc's memory allocator in this case.
261     ///
262     /// Prefer `.as_ptr()` when just retrieving a pointer to the
263     /// string data, as that does not relinquish ownership.
264     pub unsafe fn unwrap(mut self) -> *const libc::c_char {
265         self.owns_buffer_ = false;
266         self.buf
267     }
268
269     /// Return the number of bytes in the CString (not including the NUL
270     /// terminator).
271     #[inline]
272     pub fn len(&self) -> uint {
273         unsafe { libc::strlen(self.buf) as uint }
274     }
275
276     /// Returns if there are no bytes in this string
277     #[inline]
278     pub fn is_empty(&self) -> bool { self.len() == 0 }
279 }
280
281 impl Drop for CString {
282     fn drop(&mut self) {
283         if self.owns_buffer_ {
284             unsafe {
285                 libc::free(self.buf as *mut libc::c_void)
286             }
287         }
288     }
289 }
290
291 impl fmt::Show for CString {
292     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293         String::from_utf8_lossy(self.as_bytes_no_nul()).fmt(f)
294     }
295 }
296
297 /// A generic trait for converting a value to a CString.
298 pub trait ToCStr for Sized? {
299     /// Copy the receiver into a CString.
300     ///
301     /// # Failure
302     ///
303     /// Fails the task if the receiver has an interior null.
304     fn to_c_str(&self) -> CString;
305
306     /// Unsafe variant of `to_c_str()` that doesn't check for nulls.
307     unsafe fn to_c_str_unchecked(&self) -> CString;
308
309     /// Work with a temporary CString constructed from the receiver.
310     /// The provided `*libc::c_char` will be freed immediately upon return.
311     ///
312     /// # Example
313     ///
314     /// ```rust
315     /// extern crate libc;
316     ///
317     /// fn main() {
318     ///     let s = "PATH".with_c_str(|path| unsafe {
319     ///         libc::getenv(path)
320     ///     });
321     /// }
322     /// ```
323     ///
324     /// # Failure
325     ///
326     /// Fails the task if the receiver has an interior null.
327     #[inline]
328     fn with_c_str<T>(&self, f: |*const libc::c_char| -> T) -> T {
329         let c_str = self.to_c_str();
330         f(c_str.as_ptr())
331     }
332
333     /// Unsafe variant of `with_c_str()` that doesn't check for nulls.
334     #[inline]
335     unsafe fn with_c_str_unchecked<T>(&self, f: |*const libc::c_char| -> T) -> T {
336         let c_str = self.to_c_str_unchecked();
337         f(c_str.as_ptr())
338     }
339 }
340
341 impl ToCStr for str {
342     #[inline]
343     fn to_c_str(&self) -> CString {
344         self.as_bytes().to_c_str()
345     }
346
347     #[inline]
348     unsafe fn to_c_str_unchecked(&self) -> CString {
349         self.as_bytes().to_c_str_unchecked()
350     }
351
352     #[inline]
353     fn with_c_str<T>(&self, f: |*const libc::c_char| -> T) -> T {
354         self.as_bytes().with_c_str(f)
355     }
356
357     #[inline]
358     unsafe fn with_c_str_unchecked<T>(&self, f: |*const libc::c_char| -> T) -> T {
359         self.as_bytes().with_c_str_unchecked(f)
360     }
361 }
362
363 impl ToCStr for String {
364     #[inline]
365     fn to_c_str(&self) -> CString {
366         self.as_bytes().to_c_str()
367     }
368
369     #[inline]
370     unsafe fn to_c_str_unchecked(&self) -> CString {
371         self.as_bytes().to_c_str_unchecked()
372     }
373
374     #[inline]
375     fn with_c_str<T>(&self, f: |*const libc::c_char| -> T) -> T {
376         self.as_bytes().with_c_str(f)
377     }
378
379     #[inline]
380     unsafe fn with_c_str_unchecked<T>(&self, f: |*const libc::c_char| -> T) -> T {
381         self.as_bytes().with_c_str_unchecked(f)
382     }
383 }
384
385 // The length of the stack allocated buffer for `vec.with_c_str()`
386 const BUF_LEN: uint = 128;
387
388 impl ToCStr for [u8] {
389     fn to_c_str(&self) -> CString {
390         let mut cs = unsafe { self.to_c_str_unchecked() };
391         check_for_null(self, cs.as_mut_ptr());
392         cs
393     }
394
395     unsafe fn to_c_str_unchecked(&self) -> CString {
396         let self_len = self.len();
397         let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
398         if buf.is_null() { ::alloc::oom() }
399
400         ptr::copy_memory(buf, self.as_ptr(), self_len);
401         *buf.offset(self_len as int) = 0;
402
403         CString::new(buf as *const libc::c_char, true)
404     }
405
406     fn with_c_str<T>(&self, f: |*const libc::c_char| -> T) -> T {
407         unsafe { with_c_str(self, true, f) }
408     }
409
410     unsafe fn with_c_str_unchecked<T>(&self, f: |*const libc::c_char| -> T) -> T {
411         with_c_str(self, false, f)
412     }
413 }
414
415 impl<'a, Sized? T: ToCStr> ToCStr for &'a T {
416     #[inline]
417     fn to_c_str(&self) -> CString {
418         (**self).to_c_str()
419     }
420
421     #[inline]
422     unsafe fn to_c_str_unchecked(&self) -> CString {
423         (**self).to_c_str_unchecked()
424     }
425
426     #[inline]
427     fn with_c_str<T>(&self, f: |*const libc::c_char| -> T) -> T {
428         (**self).with_c_str(f)
429     }
430
431     #[inline]
432     unsafe fn with_c_str_unchecked<T>(&self, f: |*const libc::c_char| -> T) -> T {
433         (**self).with_c_str_unchecked(f)
434     }
435 }
436
437 // Unsafe function that handles possibly copying the &[u8] into a stack array.
438 unsafe fn with_c_str<T>(v: &[u8], checked: bool,
439                         f: |*const libc::c_char| -> T) -> T {
440     let c_str = if v.len() < BUF_LEN {
441         let mut buf: [u8, .. BUF_LEN] = mem::uninitialized();
442         slice::bytes::copy_memory(buf, v);
443         buf[v.len()] = 0;
444
445         let buf = buf.as_mut_ptr();
446         if checked {
447             check_for_null(v, buf as *mut libc::c_char);
448         }
449
450         return f(buf as *const libc::c_char)
451     } else if checked {
452         v.to_c_str()
453     } else {
454         v.to_c_str_unchecked()
455     };
456
457     f(c_str.as_ptr())
458 }
459
460 #[inline]
461 fn check_for_null(v: &[u8], buf: *mut libc::c_char) {
462     for i in range(0, v.len()) {
463         unsafe {
464             let p = buf.offset(i as int);
465             assert!(*p != 0);
466         }
467     }
468 }
469
470 /// External iterator for a CString's bytes.
471 ///
472 /// Use with the `std::iter` module.
473 pub struct CChars<'a> {
474     ptr: *const libc::c_char,
475     marker: marker::ContravariantLifetime<'a>,
476 }
477
478 impl<'a> Iterator<libc::c_char> for CChars<'a> {
479     fn next(&mut self) -> Option<libc::c_char> {
480         let ch = unsafe { *self.ptr };
481         if ch == 0 {
482             None
483         } else {
484             self.ptr = unsafe { self.ptr.offset(1) };
485             Some(ch)
486         }
487     }
488 }
489
490 /// Parses a C "multistring", eg windows env values or
491 /// the req->ptr result in a uv_fs_readdir() call.
492 ///
493 /// Optionally, a `count` can be passed in, limiting the
494 /// parsing to only being done `count`-times.
495 ///
496 /// The specified closure is invoked with each string that
497 /// is found, and the number of strings found is returned.
498 pub unsafe fn from_c_multistring(buf: *const libc::c_char,
499                                  count: Option<uint>,
500                                  f: |&CString|) -> uint {
501
502     let mut curr_ptr: uint = buf as uint;
503     let mut ctr = 0;
504     let (limited_count, limit) = match count {
505         Some(limit) => (true, limit),
506         None => (false, 0)
507     };
508     while ((limited_count && ctr < limit) || !limited_count)
509           && *(curr_ptr as *const libc::c_char) != 0 as libc::c_char {
510         let cstr = CString::new(curr_ptr as *const libc::c_char, false);
511         f(&cstr);
512         curr_ptr += cstr.len() + 1;
513         ctr += 1;
514     }
515     return ctr;
516 }
517
518 #[cfg(test)]
519 mod tests {
520     use std::prelude::*;
521     use std::ptr;
522     use std::task;
523     use libc;
524
525     use super::*;
526
527     #[test]
528     fn test_str_multistring_parsing() {
529         unsafe {
530             let input = b"zero\0one\0\0";
531             let ptr = input.as_ptr();
532             let expected = ["zero", "one"];
533             let mut it = expected.iter();
534             let result = from_c_multistring(ptr as *const libc::c_char, None, |c| {
535                 let cbytes = c.as_bytes_no_nul();
536                 assert_eq!(cbytes, it.next().unwrap().as_bytes());
537             });
538             assert_eq!(result, 2);
539             assert!(it.next().is_none());
540         }
541     }
542
543     #[test]
544     fn test_str_to_c_str() {
545         let c_str = "".to_c_str();
546         unsafe {
547             assert_eq!(*c_str.as_ptr().offset(0), 0);
548         }
549
550         let c_str = "hello".to_c_str();
551         let buf = c_str.as_ptr();
552         unsafe {
553             assert_eq!(*buf.offset(0), 'h' as libc::c_char);
554             assert_eq!(*buf.offset(1), 'e' as libc::c_char);
555             assert_eq!(*buf.offset(2), 'l' as libc::c_char);
556             assert_eq!(*buf.offset(3), 'l' as libc::c_char);
557             assert_eq!(*buf.offset(4), 'o' as libc::c_char);
558             assert_eq!(*buf.offset(5), 0);
559         }
560     }
561
562     #[test]
563     fn test_vec_to_c_str() {
564         let b: &[u8] = [];
565         let c_str = b.to_c_str();
566         unsafe {
567             assert_eq!(*c_str.as_ptr().offset(0), 0);
568         }
569
570         let c_str = b"hello".to_c_str();
571         let buf = c_str.as_ptr();
572         unsafe {
573             assert_eq!(*buf.offset(0), 'h' as libc::c_char);
574             assert_eq!(*buf.offset(1), 'e' as libc::c_char);
575             assert_eq!(*buf.offset(2), 'l' as libc::c_char);
576             assert_eq!(*buf.offset(3), 'l' as libc::c_char);
577             assert_eq!(*buf.offset(4), 'o' as libc::c_char);
578             assert_eq!(*buf.offset(5), 0);
579         }
580
581         let c_str = b"foo\xFF".to_c_str();
582         let buf = c_str.as_ptr();
583         unsafe {
584             assert_eq!(*buf.offset(0), 'f' as libc::c_char);
585             assert_eq!(*buf.offset(1), 'o' as libc::c_char);
586             assert_eq!(*buf.offset(2), 'o' as libc::c_char);
587             assert_eq!(*buf.offset(3), 0xffu8 as i8);
588             assert_eq!(*buf.offset(4), 0);
589         }
590     }
591
592     #[test]
593     fn test_unwrap() {
594         let c_str = "hello".to_c_str();
595         unsafe { libc::free(c_str.unwrap() as *mut libc::c_void) }
596     }
597
598     #[test]
599     fn test_as_ptr() {
600         let c_str = "hello".to_c_str();
601         let len = unsafe { libc::strlen(c_str.as_ptr()) };
602         assert_eq!(len, 5);
603     }
604
605     #[test]
606     fn test_iterator() {
607         let c_str = "".to_c_str();
608         let mut iter = c_str.iter();
609         assert_eq!(iter.next(), None);
610
611         let c_str = "hello".to_c_str();
612         let mut iter = c_str.iter();
613         assert_eq!(iter.next(), Some('h' as libc::c_char));
614         assert_eq!(iter.next(), Some('e' as libc::c_char));
615         assert_eq!(iter.next(), Some('l' as libc::c_char));
616         assert_eq!(iter.next(), Some('l' as libc::c_char));
617         assert_eq!(iter.next(), Some('o' as libc::c_char));
618         assert_eq!(iter.next(), None);
619     }
620
621     #[test]
622     fn test_to_c_str_fail() {
623         assert!(task::try(proc() { "he\x00llo".to_c_str() }).is_err());
624     }
625
626     #[test]
627     fn test_to_c_str_unchecked() {
628         unsafe {
629             let c_string = "he\x00llo".to_c_str_unchecked();
630             let buf = c_string.as_ptr();
631             assert_eq!(*buf.offset(0), 'h' as libc::c_char);
632             assert_eq!(*buf.offset(1), 'e' as libc::c_char);
633             assert_eq!(*buf.offset(2), 0);
634             assert_eq!(*buf.offset(3), 'l' as libc::c_char);
635             assert_eq!(*buf.offset(4), 'l' as libc::c_char);
636             assert_eq!(*buf.offset(5), 'o' as libc::c_char);
637             assert_eq!(*buf.offset(6), 0);
638         }
639     }
640
641     #[test]
642     fn test_as_bytes() {
643         let c_str = "hello".to_c_str();
644         assert_eq!(c_str.as_bytes(), b"hello\0");
645         let c_str = "".to_c_str();
646         assert_eq!(c_str.as_bytes(), b"\0");
647         let c_str = b"foo\xFF".to_c_str();
648         assert_eq!(c_str.as_bytes(), b"foo\xFF\0");
649     }
650
651     #[test]
652     fn test_as_bytes_no_nul() {
653         let c_str = "hello".to_c_str();
654         assert_eq!(c_str.as_bytes_no_nul(), b"hello");
655         let c_str = "".to_c_str();
656         let exp: &[u8] = [];
657         assert_eq!(c_str.as_bytes_no_nul(), exp);
658         let c_str = b"foo\xFF".to_c_str();
659         assert_eq!(c_str.as_bytes_no_nul(), b"foo\xFF");
660     }
661
662     #[test]
663     fn test_as_str() {
664         let c_str = "hello".to_c_str();
665         assert_eq!(c_str.as_str(), Some("hello"));
666         let c_str = "".to_c_str();
667         assert_eq!(c_str.as_str(), Some(""));
668         let c_str = b"foo\xFF".to_c_str();
669         assert_eq!(c_str.as_str(), None);
670     }
671
672     #[test]
673     #[should_fail]
674     fn test_new_fail() {
675         let _c_str = unsafe { CString::new(ptr::null(), false) };
676     }
677
678     #[test]
679     fn test_clone() {
680         let a = "hello".to_c_str();
681         let b = a.clone();
682         assert!(a == b);
683     }
684
685     #[test]
686     fn test_clone_noleak() {
687         fn foo(f: |c: &CString|) {
688             let s = "test".to_string();
689             let c = s.to_c_str();
690             // give the closure a non-owned CString
691             let mut c_ = unsafe { CString::new(c.as_ptr(), false) };
692             f(&c_);
693             // muck with the buffer for later printing
694             unsafe { *c_.as_mut_ptr() = 'X' as libc::c_char }
695         }
696
697         let mut c_: Option<CString> = None;
698         foo(|c| {
699             c_ = Some(c.clone());
700             c.clone();
701             // force a copy, reading the memory
702             c.as_bytes().to_vec();
703         });
704         let c_ = c_.unwrap();
705         // force a copy, reading the memory
706         c_.as_bytes().to_vec();
707     }
708 }
709
710 #[cfg(test)]
711 mod bench {
712     use test::Bencher;
713     use libc;
714     use std::prelude::*;
715
716     #[inline]
717     fn check(s: &str, c_str: *const libc::c_char) {
718         let s_buf = s.as_ptr();
719         for i in range(0, s.len()) {
720             unsafe {
721                 assert_eq!(
722                     *s_buf.offset(i as int) as libc::c_char,
723                     *c_str.offset(i as int));
724             }
725         }
726     }
727
728     static S_SHORT: &'static str = "Mary";
729     static S_MEDIUM: &'static str = "Mary had a little lamb";
730     static S_LONG: &'static str = "\
731         Mary had a little lamb, Little lamb
732         Mary had a little lamb, Little lamb
733         Mary had a little lamb, Little lamb
734         Mary had a little lamb, Little lamb
735         Mary had a little lamb, Little lamb
736         Mary had a little lamb, Little lamb";
737
738     fn bench_to_string(b: &mut Bencher, s: &str) {
739         b.iter(|| {
740             let c_str = s.to_c_str();
741             check(s, c_str.as_ptr());
742         })
743     }
744
745     #[bench]
746     fn bench_to_c_str_short(b: &mut Bencher) {
747         bench_to_string(b, S_SHORT)
748     }
749
750     #[bench]
751     fn bench_to_c_str_medium(b: &mut Bencher) {
752         bench_to_string(b, S_MEDIUM)
753     }
754
755     #[bench]
756     fn bench_to_c_str_long(b: &mut Bencher) {
757         bench_to_string(b, S_LONG)
758     }
759
760     fn bench_to_c_str_unchecked(b: &mut Bencher, s: &str) {
761         b.iter(|| {
762             let c_str = unsafe { s.to_c_str_unchecked() };
763             check(s, c_str.as_ptr())
764         })
765     }
766
767     #[bench]
768     fn bench_to_c_str_unchecked_short(b: &mut Bencher) {
769         bench_to_c_str_unchecked(b, S_SHORT)
770     }
771
772     #[bench]
773     fn bench_to_c_str_unchecked_medium(b: &mut Bencher) {
774         bench_to_c_str_unchecked(b, S_MEDIUM)
775     }
776
777     #[bench]
778     fn bench_to_c_str_unchecked_long(b: &mut Bencher) {
779         bench_to_c_str_unchecked(b, S_LONG)
780     }
781
782     fn bench_with_c_str(b: &mut Bencher, s: &str) {
783         b.iter(|| {
784             s.with_c_str(|c_str_buf| check(s, c_str_buf))
785         })
786     }
787
788     #[bench]
789     fn bench_with_c_str_short(b: &mut Bencher) {
790         bench_with_c_str(b, S_SHORT)
791     }
792
793     #[bench]
794     fn bench_with_c_str_medium(b: &mut Bencher) {
795         bench_with_c_str(b, S_MEDIUM)
796     }
797
798     #[bench]
799     fn bench_with_c_str_long(b: &mut Bencher) {
800         bench_with_c_str(b, S_LONG)
801     }
802
803     fn bench_with_c_str_unchecked(b: &mut Bencher, s: &str) {
804         b.iter(|| {
805             unsafe {
806                 s.with_c_str_unchecked(|c_str_buf| check(s, c_str_buf))
807             }
808         })
809     }
810
811     #[bench]
812     fn bench_with_c_str_unchecked_short(b: &mut Bencher) {
813         bench_with_c_str_unchecked(b, S_SHORT)
814     }
815
816     #[bench]
817     fn bench_with_c_str_unchecked_medium(b: &mut Bencher) {
818         bench_with_c_str_unchecked(b, S_MEDIUM)
819     }
820
821     #[bench]
822     fn bench_with_c_str_unchecked_long(b: &mut Bencher) {
823         bench_with_c_str_unchecked(b, S_LONG)
824     }
825 }