2013-08-03 19:13:14 -05:00
|
|
|
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2014-11-25 20:17:11 -06:00
|
|
|
//! C-string manipulation and management
|
|
|
|
//!
|
|
|
|
//! This modules provides the basic methods for creating and manipulating
|
|
|
|
//! null-terminated strings for use with FFI calls (back to C). Most C APIs require
|
|
|
|
//! that the string being passed to them is null-terminated, and by default rust's
|
|
|
|
//! string types are *not* null terminated.
|
|
|
|
//!
|
|
|
|
//! The other problem with translating Rust strings to C strings is that Rust
|
|
|
|
//! strings can validly contain a null-byte in the middle of the string (0 is a
|
|
|
|
//! valid Unicode codepoint). This means that not all Rust strings can actually be
|
|
|
|
//! translated to C strings.
|
|
|
|
//!
|
|
|
|
//! # Creation of a C string
|
|
|
|
//!
|
|
|
|
//! A C string is managed through the `CString` type defined in this module. It
|
|
|
|
//! "owns" the internal buffer of characters and will automatically deallocate the
|
|
|
|
//! buffer when the string is dropped. The `ToCStr` trait is implemented for `&str`
|
|
|
|
//! and `&[u8]`, but the conversions can fail due to some of the limitations
|
|
|
|
//! explained above.
|
|
|
|
//!
|
|
|
|
//! This also means that currently whenever a C string is created, an allocation
|
|
|
|
//! must be performed to place the data elsewhere (the lifetime of the C string is
|
|
|
|
//! not tied to the lifetime of the original string/data buffer). If C strings are
|
|
|
|
//! heavily used in applications, then caching may be advisable to prevent
|
|
|
|
//! unnecessary amounts of allocations.
|
|
|
|
//!
|
|
|
|
//! Be carefull to remember that the memory is managed by C allocator API and not
|
|
|
|
//! by Rust allocator API.
|
|
|
|
//! That means that the CString pointers should be freed with C allocator API
|
|
|
|
//! if you intend to do that on your own, as the behaviour if you free them with
|
|
|
|
//! Rust's allocator API is not well defined
|
|
|
|
//!
|
|
|
|
//! An example of creating and using a C string would be:
|
|
|
|
//!
|
|
|
|
//! ```rust
|
|
|
|
//! extern crate libc;
|
|
|
|
//!
|
|
|
|
//! extern {
|
|
|
|
//! fn puts(s: *const libc::c_char);
|
|
|
|
//! }
|
|
|
|
//!
|
|
|
|
//! fn main() {
|
|
|
|
//! let my_string = "Hello, world!";
|
|
|
|
//!
|
|
|
|
//! // Allocate the C string with an explicit local that owns the string. The
|
|
|
|
//! // `c_buffer` pointer will be deallocated when `my_c_string` goes out of scope.
|
|
|
|
//! let my_c_string = my_string.to_c_str();
|
|
|
|
//! unsafe {
|
|
|
|
//! puts(my_c_string.as_ptr());
|
|
|
|
//! }
|
|
|
|
//!
|
|
|
|
//! // Don't save/return the pointer to the C string, the `c_buffer` will be
|
|
|
|
//! // deallocated when this block returns!
|
|
|
|
//! my_string.with_c_str(|c_buffer| {
|
|
|
|
//! unsafe { puts(c_buffer); }
|
|
|
|
//! });
|
|
|
|
//! }
|
|
|
|
//! ```
|
2013-09-17 21:42:07 -05:00
|
|
|
|
2014-11-23 21:21:17 -06:00
|
|
|
use string::String;
|
|
|
|
use hash;
|
|
|
|
use fmt;
|
|
|
|
use kinds::marker;
|
|
|
|
use mem;
|
2014-12-11 11:44:17 -06:00
|
|
|
use core::prelude::*;
|
|
|
|
|
2014-11-23 21:21:17 -06:00
|
|
|
use ptr;
|
|
|
|
use raw::Slice;
|
|
|
|
use slice;
|
|
|
|
use str;
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
use libc;
|
2013-08-14 21:19:29 -05:00
|
|
|
|
2013-08-04 20:37:55 -05:00
|
|
|
/// The representation of a C String.
|
|
|
|
///
|
|
|
|
/// This structure wraps a `*libc::c_char`, and will automatically free the
|
|
|
|
/// memory it is pointing to when it goes out of scope.
|
librustc: Make `Copy` opt-in.
This change makes the compiler no longer infer whether types (structures
and enumerations) implement the `Copy` trait (and thus are implicitly
copyable). Rather, you must implement `Copy` yourself via `impl Copy for
MyType {}`.
A new warning has been added, `missing_copy_implementations`, to warn
you if a non-generic public type has been added that could have
implemented `Copy` but didn't.
For convenience, you may *temporarily* opt out of this behavior by using
`#![feature(opt_out_copy)]`. Note though that this feature gate will never be
accepted and will be removed by the time that 1.0 is released, so you should
transition your code away from using it.
This breaks code like:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
Change this code to:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
impl Copy for Point2D {}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
This is the backwards-incompatible part of #13231.
Part of RFC #3.
[breaking-change]
2014-12-05 19:01:33 -06:00
|
|
|
#[allow(missing_copy_implementations)]
|
2013-08-03 19:13:14 -05:00
|
|
|
pub struct CString {
|
2014-06-25 14:47:34 -05:00
|
|
|
buf: *const libc::c_char,
|
2014-03-27 17:09:47 -05:00
|
|
|
owns_buffer_: bool,
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
|
2014-01-27 05:12:59 -06:00
|
|
|
impl Clone for CString {
|
|
|
|
/// Clone this CString into a new, uniquely owned CString. For safety
|
2014-09-17 17:35:26 -05:00
|
|
|
/// reasons, this is always a deep clone with the memory allocated
|
2014-09-18 05:13:30 -05:00
|
|
|
/// with C's allocator API, rather than the usual shallow clone.
|
2014-01-27 05:12:59 -06:00
|
|
|
fn clone(&self) -> CString {
|
2014-07-22 11:24:33 -05:00
|
|
|
let len = self.len() + 1;
|
2014-10-24 16:34:57 -05:00
|
|
|
let buf = unsafe { libc::malloc(len as libc::size_t) } as *mut libc::c_char;
|
2014-10-28 16:06:06 -05:00
|
|
|
if buf.is_null() { ::alloc::oom() }
|
2014-07-22 11:24:33 -05:00
|
|
|
unsafe { ptr::copy_nonoverlapping_memory(buf, self.buf, len); }
|
|
|
|
CString { buf: buf as *const libc::c_char, owns_buffer_: true }
|
2014-01-27 05:12:59 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-29 19:45:07 -05:00
|
|
|
impl PartialEq for CString {
|
2014-01-30 18:10:07 -06:00
|
|
|
fn eq(&self, other: &CString) -> bool {
|
2014-07-22 11:24:33 -05:00
|
|
|
// Check if the two strings share the same buffer
|
2014-01-30 18:10:07 -06:00
|
|
|
if self.buf as uint == other.buf as uint {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
unsafe {
|
|
|
|
libc::strcmp(self.buf, other.buf) == 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-02 15:50:45 -05:00
|
|
|
impl PartialOrd for CString {
|
2014-10-29 20:21:37 -05:00
|
|
|
#[inline]
|
|
|
|
fn partial_cmp(&self, other: &CString) -> Option<Ordering> {
|
|
|
|
self.as_bytes().partial_cmp(other.as_bytes())
|
|
|
|
}
|
2014-07-02 15:50:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for CString {}
|
|
|
|
|
|
|
|
impl<S: hash::Writer> hash::Hash<S> for CString {
|
|
|
|
#[inline]
|
|
|
|
fn hash(&self, state: &mut S) {
|
|
|
|
self.as_bytes().hash(state)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-05 21:55:07 -05:00
|
|
|
impl CString {
|
2014-09-18 05:13:30 -05:00
|
|
|
/// Create a C String from a pointer, with memory managed by C's allocator
|
2014-09-18 07:16:26 -05:00
|
|
|
/// API, so avoid calling it with a pointer to memory managed by Rust's
|
|
|
|
/// allocator API, as the behaviour would not be well defined.
|
2014-07-22 11:24:33 -05:00
|
|
|
///
|
2014-11-11 12:36:09 -06:00
|
|
|
///# Panics
|
2014-07-22 11:24:33 -05:00
|
|
|
///
|
2014-11-11 12:36:09 -06:00
|
|
|
/// Panics if `buf` is null
|
2014-06-25 14:47:34 -05:00
|
|
|
pub unsafe fn new(buf: *const libc::c_char, owns_buffer: bool) -> CString {
|
2014-07-22 11:24:33 -05:00
|
|
|
assert!(!buf.is_null());
|
2013-08-04 20:37:55 -05:00
|
|
|
CString { buf: buf, owns_buffer_: owns_buffer }
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
|
2014-06-14 07:46:50 -05:00
|
|
|
/// Return a pointer to the NUL-terminated string data.
|
|
|
|
///
|
|
|
|
/// `.as_ptr` returns an internal pointer into the `CString`, and
|
|
|
|
/// may be invalidated when the `CString` falls out of scope (the
|
|
|
|
/// destructor will run, freeing the allocation if there is
|
|
|
|
/// one).
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// let foo = "some string";
|
|
|
|
///
|
|
|
|
/// // right
|
|
|
|
/// let x = foo.to_c_str();
|
|
|
|
/// let p = x.as_ptr();
|
|
|
|
///
|
|
|
|
/// // wrong (the CString will be freed, invalidating `p`)
|
|
|
|
/// let p = foo.to_c_str().as_ptr();
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// extern crate libc;
|
|
|
|
///
|
|
|
|
/// fn main() {
|
|
|
|
/// let c_str = "foo bar".to_c_str();
|
|
|
|
/// unsafe {
|
|
|
|
/// libc::puts(c_str.as_ptr());
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
pub fn as_ptr(&self) -> *const libc::c_char {
|
|
|
|
self.buf
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return a mutable pointer to the NUL-terminated string data.
|
|
|
|
///
|
|
|
|
/// `.as_mut_ptr` returns an internal pointer into the `CString`, and
|
|
|
|
/// may be invalidated when the `CString` falls out of scope (the
|
|
|
|
/// destructor will run, freeing the allocation if there is
|
|
|
|
/// one).
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// let foo = "some string";
|
|
|
|
///
|
|
|
|
/// // right
|
|
|
|
/// let mut x = foo.to_c_str();
|
|
|
|
/// let p = x.as_mut_ptr();
|
|
|
|
///
|
|
|
|
/// // wrong (the CString will be freed, invalidating `p`)
|
|
|
|
/// let p = foo.to_c_str().as_mut_ptr();
|
|
|
|
/// ```
|
|
|
|
pub fn as_mut_ptr(&mut self) -> *mut libc::c_char {
|
|
|
|
self.buf as *mut _
|
|
|
|
}
|
|
|
|
|
2013-08-04 20:37:55 -05:00
|
|
|
/// Returns whether or not the `CString` owns the buffer.
|
|
|
|
pub fn owns_buffer(&self) -> bool {
|
|
|
|
self.owns_buffer_
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Converts the CString into a `&[u8]` without copying.
|
2014-02-14 17:42:35 -06:00
|
|
|
/// Includes the terminating NUL byte.
|
2013-09-15 22:30:03 -05:00
|
|
|
#[inline]
|
2013-08-05 21:55:07 -05:00
|
|
|
pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
|
2013-08-03 19:13:14 -05:00
|
|
|
unsafe {
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
mem::transmute(Slice { data: self.buf, len: self.len() + 1 })
|
2014-02-14 17:42:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Converts the CString into a `&[u8]` without copying.
|
|
|
|
/// Does not include the terminating NUL byte.
|
|
|
|
#[inline]
|
|
|
|
pub fn as_bytes_no_nul<'a>(&'a self) -> &'a [u8] {
|
|
|
|
unsafe {
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
mem::transmute(Slice { data: self.buf, len: self.len() })
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-15 22:30:03 -05:00
|
|
|
/// Converts the CString into a `&str` without copying.
|
2014-01-30 17:29:36 -06:00
|
|
|
/// Returns None if the CString is not UTF-8.
|
2013-09-15 22:30:03 -05:00
|
|
|
#[inline]
|
|
|
|
pub fn as_str<'a>(&'a self) -> Option<&'a str> {
|
2014-02-14 17:42:35 -06:00
|
|
|
let buf = self.as_bytes_no_nul();
|
2013-12-23 10:30:49 -06:00
|
|
|
str::from_utf8(buf)
|
2013-09-15 22:30:03 -05:00
|
|
|
}
|
|
|
|
|
2013-08-04 20:37:55 -05:00
|
|
|
/// Return a CString iterator.
|
2014-01-14 21:32:24 -06:00
|
|
|
pub fn iter<'a>(&'a self) -> CChars<'a> {
|
|
|
|
CChars {
|
2013-08-03 19:13:14 -05:00
|
|
|
ptr: self.buf,
|
2014-01-22 13:03:02 -06:00
|
|
|
marker: marker::ContravariantLifetime,
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-14 17:42:55 -05:00
|
|
|
|
|
|
|
/// Unwraps the wrapped `*libc::c_char` from the `CString` wrapper.
|
|
|
|
///
|
|
|
|
/// Any ownership of the buffer by the `CString` wrapper is
|
|
|
|
/// forgotten, meaning that the backing allocation of this
|
|
|
|
/// `CString` is not automatically freed if it owns the
|
|
|
|
/// allocation. In this case, a user of `.unwrap()` should ensure
|
2014-09-18 07:16:26 -05:00
|
|
|
/// the allocation is freed, to avoid leaking memory. You should
|
2014-09-17 17:35:26 -05:00
|
|
|
/// use libc's memory allocator in this case.
|
2014-06-14 17:42:55 -05:00
|
|
|
///
|
|
|
|
/// Prefer `.as_ptr()` when just retrieving a pointer to the
|
|
|
|
/// string data, as that does not relinquish ownership.
|
2014-11-20 11:23:43 -06:00
|
|
|
pub unsafe fn into_inner(mut self) -> *const libc::c_char {
|
2014-06-14 17:42:55 -05:00
|
|
|
self.owns_buffer_ = false;
|
|
|
|
self.buf
|
|
|
|
}
|
|
|
|
|
2014-11-20 11:23:43 -06:00
|
|
|
/// Deprecated, use into_inner() instead
|
|
|
|
#[deprecated = "renamed to into_inner()"]
|
|
|
|
pub unsafe fn unwrap(self) -> *const libc::c_char { self.into_inner() }
|
|
|
|
|
2014-10-30 15:43:24 -05:00
|
|
|
/// Return the number of bytes in the CString (not including the NUL
|
|
|
|
/// terminator).
|
|
|
|
#[inline]
|
|
|
|
pub fn len(&self) -> uint {
|
|
|
|
unsafe { libc::strlen(self.buf) as uint }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns if there are no bytes in this string
|
|
|
|
#[inline]
|
|
|
|
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for CString {
|
2013-09-16 20:18:07 -05:00
|
|
|
fn drop(&mut self) {
|
2013-08-06 23:08:39 -05:00
|
|
|
if self.owns_buffer_ {
|
2013-08-03 19:13:14 -05:00
|
|
|
unsafe {
|
2014-01-21 08:31:32 -06:00
|
|
|
libc::free(self.buf as *mut libc::c_void)
|
2013-08-04 20:37:55 -05:00
|
|
|
}
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-21 11:06:24 -05:00
|
|
|
impl fmt::Show for CString {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
String::from_utf8_lossy(self.as_bytes_no_nul()).fmt(f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-04 20:37:55 -05:00
|
|
|
/// A generic trait for converting a value to a CString.
|
2014-10-24 19:23:22 -05:00
|
|
|
pub trait ToCStr for Sized? {
|
2013-08-14 21:19:29 -05:00
|
|
|
/// Copy the receiver into a CString.
|
|
|
|
///
|
2014-11-11 12:36:09 -06:00
|
|
|
/// # Panics
|
2013-08-14 21:19:29 -05:00
|
|
|
///
|
2014-11-11 12:36:09 -06:00
|
|
|
/// Panics the task if the receiver has an interior null.
|
2013-08-03 19:13:14 -05:00
|
|
|
fn to_c_str(&self) -> CString;
|
2013-08-14 21:19:29 -05:00
|
|
|
|
|
|
|
/// Unsafe variant of `to_c_str()` that doesn't check for nulls.
|
|
|
|
unsafe fn to_c_str_unchecked(&self) -> CString;
|
2013-08-14 21:21:59 -05:00
|
|
|
|
|
|
|
/// Work with a temporary CString constructed from the receiver.
|
|
|
|
/// The provided `*libc::c_char` will be freed immediately upon return.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
2013-09-23 19:20:36 -05:00
|
|
|
/// ```rust
|
2014-02-26 11:58:41 -06:00
|
|
|
/// extern crate libc;
|
2013-12-22 15:31:23 -06:00
|
|
|
///
|
2014-02-26 11:58:41 -06:00
|
|
|
/// fn main() {
|
|
|
|
/// let s = "PATH".with_c_str(|path| unsafe {
|
|
|
|
/// libc::getenv(path)
|
|
|
|
/// });
|
|
|
|
/// }
|
2013-09-23 19:20:36 -05:00
|
|
|
/// ```
|
2013-08-14 21:21:59 -05:00
|
|
|
///
|
2014-11-11 12:36:09 -06:00
|
|
|
/// # Panics
|
2013-08-14 21:21:59 -05:00
|
|
|
///
|
2014-11-11 12:36:09 -06:00
|
|
|
/// Panics the task if the receiver has an interior null.
|
2013-08-14 21:21:59 -05:00
|
|
|
#[inline]
|
2014-12-07 12:22:04 -06:00
|
|
|
fn with_c_str<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2014-06-14 07:50:07 -05:00
|
|
|
let c_str = self.to_c_str();
|
|
|
|
f(c_str.as_ptr())
|
2013-08-14 21:21:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Unsafe variant of `with_c_str()` that doesn't check for nulls.
|
|
|
|
#[inline]
|
2014-12-07 12:22:04 -06:00
|
|
|
unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2014-06-14 07:50:07 -05:00
|
|
|
let c_str = self.to_c_str_unchecked();
|
|
|
|
f(c_str.as_ptr())
|
2013-08-14 21:21:59 -05:00
|
|
|
}
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
|
2014-10-24 19:23:22 -05:00
|
|
|
impl ToCStr for str {
|
2013-08-04 20:37:55 -05:00
|
|
|
#[inline]
|
2013-08-03 19:13:14 -05:00
|
|
|
fn to_c_str(&self) -> CString {
|
|
|
|
self.as_bytes().to_c_str()
|
|
|
|
}
|
2013-08-14 21:19:29 -05:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn to_c_str_unchecked(&self) -> CString {
|
|
|
|
self.as_bytes().to_c_str_unchecked()
|
|
|
|
}
|
2013-09-18 14:32:35 -05:00
|
|
|
|
|
|
|
#[inline]
|
2014-12-07 12:22:04 -06:00
|
|
|
fn with_c_str<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2013-09-18 14:32:35 -05:00
|
|
|
self.as_bytes().with_c_str(f)
|
|
|
|
}
|
2013-09-20 18:48:07 -05:00
|
|
|
|
|
|
|
#[inline]
|
2014-12-07 12:22:04 -06:00
|
|
|
unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2013-09-20 18:48:07 -05:00
|
|
|
self.as_bytes().with_c_str_unchecked(f)
|
|
|
|
}
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
|
2014-05-22 18:57:53 -05:00
|
|
|
impl ToCStr for String {
|
2014-05-05 16:15:11 -05:00
|
|
|
#[inline]
|
|
|
|
fn to_c_str(&self) -> CString {
|
|
|
|
self.as_bytes().to_c_str()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn to_c_str_unchecked(&self) -> CString {
|
|
|
|
self.as_bytes().to_c_str_unchecked()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2014-12-07 12:22:04 -06:00
|
|
|
fn with_c_str<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2014-05-05 16:15:11 -05:00
|
|
|
self.as_bytes().with_c_str(f)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2014-12-07 12:22:04 -06:00
|
|
|
unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2014-05-05 16:15:11 -05:00
|
|
|
self.as_bytes().with_c_str_unchecked(f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-18 14:32:35 -05:00
|
|
|
// The length of the stack allocated buffer for `vec.with_c_str()`
|
2014-10-06 18:35:11 -05:00
|
|
|
const BUF_LEN: uint = 128;
|
2013-09-18 14:32:35 -05:00
|
|
|
|
2014-10-24 19:23:22 -05:00
|
|
|
impl ToCStr for [u8] {
|
2013-08-03 19:13:14 -05:00
|
|
|
fn to_c_str(&self) -> CString {
|
2013-08-14 21:19:29 -05:00
|
|
|
let mut cs = unsafe { self.to_c_str_unchecked() };
|
2014-10-24 19:23:22 -05:00
|
|
|
check_for_null(self, cs.as_mut_ptr());
|
2013-08-14 21:19:29 -05:00
|
|
|
cs
|
|
|
|
}
|
2013-08-03 19:13:14 -05:00
|
|
|
|
2013-08-14 21:19:29 -05:00
|
|
|
unsafe fn to_c_str_unchecked(&self) -> CString {
|
2013-12-17 08:49:31 -06:00
|
|
|
let self_len = self.len();
|
2014-10-24 16:34:57 -05:00
|
|
|
let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8;
|
2014-10-28 16:06:06 -05:00
|
|
|
if buf.is_null() { ::alloc::oom() }
|
2013-08-14 21:19:29 -05:00
|
|
|
|
2013-12-17 08:49:31 -06:00
|
|
|
ptr::copy_memory(buf, self.as_ptr(), self_len);
|
2014-02-10 15:50:42 -06:00
|
|
|
*buf.offset(self_len as int) = 0;
|
2013-08-14 21:19:29 -05:00
|
|
|
|
2014-06-25 14:47:34 -05:00
|
|
|
CString::new(buf as *const libc::c_char, true)
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
2013-09-18 14:32:35 -05:00
|
|
|
|
2014-12-07 12:22:04 -06:00
|
|
|
fn with_c_str<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2014-10-24 19:23:22 -05:00
|
|
|
unsafe { with_c_str(self, true, f) }
|
|
|
|
}
|
|
|
|
|
2014-12-07 12:22:04 -06:00
|
|
|
unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2014-10-24 19:23:22 -05:00
|
|
|
with_c_str(self, false, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, Sized? T: ToCStr> ToCStr for &'a T {
|
|
|
|
#[inline]
|
|
|
|
fn to_c_str(&self) -> CString {
|
|
|
|
(**self).to_c_str()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
unsafe fn to_c_str_unchecked(&self) -> CString {
|
|
|
|
(**self).to_c_str_unchecked()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2014-12-07 12:22:04 -06:00
|
|
|
fn with_c_str<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2014-10-24 19:23:22 -05:00
|
|
|
(**self).with_c_str(f)
|
2013-09-18 14:32:35 -05:00
|
|
|
}
|
2013-09-20 18:48:07 -05:00
|
|
|
|
2014-10-24 19:23:22 -05:00
|
|
|
#[inline]
|
2014-12-07 12:22:04 -06:00
|
|
|
unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2014-10-24 19:23:22 -05:00
|
|
|
(**self).with_c_str_unchecked(f)
|
2013-09-27 00:49:10 -05:00
|
|
|
}
|
|
|
|
}
|
2013-09-20 18:48:07 -05:00
|
|
|
|
2013-09-27 00:49:10 -05:00
|
|
|
// Unsafe function that handles possibly copying the &[u8] into a stack array.
|
2014-12-07 12:22:04 -06:00
|
|
|
unsafe fn with_c_str<T, F>(v: &[u8], checked: bool, f: F) -> T where
|
|
|
|
F: FnOnce(*const libc::c_char) -> T,
|
|
|
|
{
|
2014-06-14 07:50:07 -05:00
|
|
|
let c_str = if v.len() < BUF_LEN {
|
2014-05-23 22:53:56 -05:00
|
|
|
let mut buf: [u8, .. BUF_LEN] = mem::uninitialized();
|
2014-11-17 02:39:01 -06:00
|
|
|
slice::bytes::copy_memory(&mut buf, v);
|
2013-09-27 00:49:10 -05:00
|
|
|
buf[v.len()] = 0;
|
2013-09-20 18:48:07 -05:00
|
|
|
|
2013-12-17 09:13:20 -06:00
|
|
|
let buf = buf.as_mut_ptr();
|
|
|
|
if checked {
|
|
|
|
check_for_null(v, buf as *mut libc::c_char);
|
|
|
|
}
|
2013-09-27 00:49:10 -05:00
|
|
|
|
2014-06-14 07:50:07 -05:00
|
|
|
return f(buf as *const libc::c_char)
|
2013-09-27 00:49:10 -05:00
|
|
|
} else if checked {
|
2014-06-14 07:50:07 -05:00
|
|
|
v.to_c_str()
|
2013-09-27 00:49:10 -05:00
|
|
|
} else {
|
2014-06-14 07:50:07 -05:00
|
|
|
v.to_c_str_unchecked()
|
|
|
|
};
|
|
|
|
|
|
|
|
f(c_str.as_ptr())
|
2013-09-18 14:32:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn check_for_null(v: &[u8], buf: *mut libc::c_char) {
|
|
|
|
for i in range(0, v.len()) {
|
|
|
|
unsafe {
|
|
|
|
let p = buf.offset(i as int);
|
Remove std::condition
This has been a long time coming. Conditions in rust were initially envisioned
as being a good alternative to error code return pattern. The idea is that all
errors are fatal-by-default, and you can opt-in to handling the error by
registering an error handler.
While sounding nice, conditions ended up having some unforseen shortcomings:
* Actually handling an error has some very awkward syntax:
let mut result = None;
let mut answer = None;
io::io_error::cond.trap(|e| { result = Some(e) }).inside(|| {
answer = Some(some_io_operation());
});
match result {
Some(err) => { /* hit an I/O error */ }
None => {
let answer = answer.unwrap();
/* deal with the result of I/O */
}
}
This pattern can certainly use functions like io::result, but at its core
actually handling conditions is fairly difficult
* The "zero value" of a function is often confusing. One of the main ideas
behind using conditions was to change the signature of I/O functions. Instead
of read_be_u32() returning a result, it returned a u32. Errors were notified
via a condition, and if you caught the condition you understood that the "zero
value" returned is actually a garbage value. These zero values are often
difficult to understand, however.
One case of this is the read_bytes() function. The function takes an integer
length of the amount of bytes to read, and returns an array of that size. The
array may actually be shorter, however, if an error occurred.
Another case is fs::stat(). The theoretical "zero value" is a blank stat
struct, but it's a little awkward to create and return a zero'd out stat
struct on a call to stat().
In general, the return value of functions that can raise error are much more
natural when using a Result as opposed to an always-usable zero-value.
* Conditions impose a necessary runtime requirement on *all* I/O. In theory I/O
is as simple as calling read() and write(), but using conditions imposed the
restriction that a rust local task was required if you wanted to catch errors
with I/O. While certainly an surmountable difficulty, this was always a bit of
a thorn in the side of conditions.
* Functions raising conditions are not always clear that they are raising
conditions. This suffers a similar problem to exceptions where you don't
actually know whether a function raises a condition or not. The documentation
likely explains, but if someone retroactively adds a condition to a function
there's nothing forcing upstream users to acknowledge a new point of task
failure.
* Libaries using I/O are not guaranteed to correctly raise on conditions when an
error occurs. In developing various I/O libraries, it's much easier to just
return `None` from a read rather than raising an error. The silent contract of
"don't raise on EOF" was a little difficult to understand and threw a wrench
into the answer of the question "when do I raise a condition?"
Many of these difficulties can be overcome through documentation, examples, and
general practice. In the end, all of these difficulties added together ended up
being too overwhelming and improving various aspects didn't end up helping that
much.
A result-based I/O error handling strategy also has shortcomings, but the
cognitive burden is much smaller. The tooling necessary to make this strategy as
usable as conditions were is much smaller than the tooling necessary for
conditions.
Perhaps conditions may manifest themselves as a future entity, but for now
we're going to remove them from the standard library.
Closes #9795
Closes #8968
2014-02-04 21:02:10 -06:00
|
|
|
assert!(*p != 0);
|
2013-09-18 14:32:35 -05:00
|
|
|
}
|
|
|
|
}
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
|
2013-08-04 20:37:55 -05:00
|
|
|
/// External iterator for a CString's bytes.
|
|
|
|
///
|
2013-11-03 17:01:00 -06:00
|
|
|
/// Use with the `std::iter` module.
|
2014-01-14 21:32:24 -06:00
|
|
|
pub struct CChars<'a> {
|
2014-06-25 14:47:34 -05:00
|
|
|
ptr: *const libc::c_char,
|
2014-03-27 17:09:47 -05:00
|
|
|
marker: marker::ContravariantLifetime<'a>,
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
|
2014-01-14 21:32:24 -06:00
|
|
|
impl<'a> Iterator<libc::c_char> for CChars<'a> {
|
2013-08-03 19:13:14 -05:00
|
|
|
fn next(&mut self) -> Option<libc::c_char> {
|
2013-08-06 23:06:12 -05:00
|
|
|
let ch = unsafe { *self.ptr };
|
|
|
|
if ch == 0 {
|
2013-08-03 19:13:14 -05:00
|
|
|
None
|
|
|
|
} else {
|
2014-02-10 15:50:42 -06:00
|
|
|
self.ptr = unsafe { self.ptr.offset(1) };
|
2013-08-03 19:13:14 -05:00
|
|
|
Some(ch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-16 18:48:30 -05:00
|
|
|
/// Parses a C "multistring", eg windows env values or
|
|
|
|
/// the req->ptr result in a uv_fs_readdir() call.
|
|
|
|
///
|
|
|
|
/// Optionally, a `count` can be passed in, limiting the
|
|
|
|
/// parsing to only being done `count`-times.
|
|
|
|
///
|
|
|
|
/// The specified closure is invoked with each string that
|
|
|
|
/// is found, and the number of strings found is returned.
|
2014-12-07 12:22:04 -06:00
|
|
|
pub unsafe fn from_c_multistring<F>(buf: *const libc::c_char,
|
|
|
|
count: Option<uint>,
|
|
|
|
mut f: F)
|
|
|
|
-> uint where
|
|
|
|
F: FnMut(&CString),
|
|
|
|
{
|
2013-10-16 18:48:30 -05:00
|
|
|
|
|
|
|
let mut curr_ptr: uint = buf as uint;
|
|
|
|
let mut ctr = 0;
|
|
|
|
let (limited_count, limit) = match count {
|
|
|
|
Some(limit) => (true, limit),
|
|
|
|
None => (false, 0)
|
|
|
|
};
|
|
|
|
while ((limited_count && ctr < limit) || !limited_count)
|
2014-06-25 14:47:34 -05:00
|
|
|
&& *(curr_ptr as *const libc::c_char) != 0 as libc::c_char {
|
|
|
|
let cstr = CString::new(curr_ptr as *const libc::c_char, false);
|
2013-10-16 18:48:30 -05:00
|
|
|
f(&cstr);
|
|
|
|
curr_ptr += cstr.len() + 1;
|
|
|
|
ctr += 1;
|
|
|
|
}
|
|
|
|
return ctr;
|
|
|
|
}
|
|
|
|
|
2013-08-03 19:13:14 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2014-11-23 21:21:17 -06:00
|
|
|
use prelude::*;
|
|
|
|
use ptr;
|
|
|
|
use task;
|
2013-08-03 19:13:14 -05:00
|
|
|
use libc;
|
std: Extract librustrt out of libstd
As part of the libstd facade efforts, this commit extracts the runtime interface
out of the standard library into a standalone crate, librustrt. This crate will
provide the following services:
* Definition of the rtio interface
* Definition of the Runtime interface
* Implementation of the Task structure
* Implementation of task-local-data
* Implementation of task failure via unwinding via libunwind
* Implementation of runtime initialization and shutdown
* Implementation of thread-local-storage for the local rust Task
Notably, this crate avoids the following services:
* Thread creation and destruction. The crate does not require the knowledge of
an OS threading system, and as a result it seemed best to leave out the
`rt::thread` module from librustrt. The librustrt module does depend on
mutexes, however.
* Implementation of backtraces. There is no inherent requirement for the runtime
to be able to generate backtraces. As will be discussed later, this
functionality continues to live in libstd rather than librustrt.
As usual, a number of architectural changes were required to make this crate
possible. Users of "stable" functionality will not be impacted by this change,
but users of the `std::rt` module will likely note the changes. A list of
architectural changes made is:
* The stdout/stderr handles no longer live directly inside of the `Task`
structure. This is a consequence of librustrt not knowing about `std::io`.
These two handles are now stored inside of task-local-data.
The handles were originally stored inside of the `Task` for perf reasons, and
TLD is not currently as fast as it could be. For comparison, 100k prints goes
from 59ms to 68ms (a 15% slowdown). This appeared to me to be an acceptable
perf loss for the successful extraction of a librustrt crate.
* The `rtio` module was forced to duplicate more functionality of `std::io`. As
the module no longer depends on `std::io`, `rtio` now defines structures such
as socket addresses, addrinfo fiddly bits, etc. The primary change made was
that `rtio` now defines its own `IoError` type. This type is distinct from
`std::io::IoError` in that it does not have an enum for what error occurred,
but rather a platform-specific error code.
The native and green libraries will be updated in later commits for this
change, and the bulk of this effort was put behind updating the two libraries
for this change (with `rtio`).
* Printing a message on task failure (along with the backtrace) continues to
live in libstd, not in librustrt. This is a consequence of the above decision
to move the stdout/stderr handles to TLD rather than inside the `Task` itself.
The unwinding API now supports registration of global callback functions which
will be invoked when a task fails, allowing for libstd to register a function
to print a message and a backtrace.
The API for registering a callback is experimental and unsafe, as the
ramifications of running code on unwinding is pretty hairy.
* The `std::unstable::mutex` module has moved to `std::rt::mutex`.
* The `std::unstable::sync` module has been moved to `std::rt::exclusive` and
the type has been rewritten to not internally have an Arc and to have an RAII
guard structure when locking. Old code should stop using `Exclusive` in favor
of the primitives in `libsync`, but if necessary, old code should port to
`Arc<Exclusive<T>>`.
* The local heap has been stripped down to have fewer debugging options. None of
these were tested, and none of these have been used in a very long time.
[breaking-change]
2014-06-03 21:11:49 -05:00
|
|
|
|
|
|
|
use super::*;
|
2013-08-03 19:13:14 -05:00
|
|
|
|
2013-10-16 18:48:30 -05:00
|
|
|
#[test]
|
|
|
|
fn test_str_multistring_parsing() {
|
|
|
|
unsafe {
|
2014-06-18 13:25:36 -05:00
|
|
|
let input = b"zero\0one\0\0";
|
2013-12-15 06:35:12 -06:00
|
|
|
let ptr = input.as_ptr();
|
2013-10-16 18:48:30 -05:00
|
|
|
let expected = ["zero", "one"];
|
|
|
|
let mut it = expected.iter();
|
2014-06-25 14:47:34 -05:00
|
|
|
let result = from_c_multistring(ptr as *const libc::c_char, None, |c| {
|
2014-02-14 17:42:35 -06:00
|
|
|
let cbytes = c.as_bytes_no_nul();
|
2013-10-16 19:05:28 -05:00
|
|
|
assert_eq!(cbytes, it.next().unwrap().as_bytes());
|
2013-11-20 16:17:12 -06:00
|
|
|
});
|
2013-10-16 18:48:30 -05:00
|
|
|
assert_eq!(result, 2);
|
|
|
|
assert!(it.next().is_none());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-03 19:13:14 -05:00
|
|
|
#[test]
|
2013-09-15 22:30:03 -05:00
|
|
|
fn test_str_to_c_str() {
|
2014-06-14 07:50:07 -05:00
|
|
|
let c_str = "".to_c_str();
|
|
|
|
unsafe {
|
|
|
|
assert_eq!(*c_str.as_ptr().offset(0), 0);
|
|
|
|
}
|
2013-08-03 19:13:14 -05:00
|
|
|
|
2014-06-14 07:50:07 -05:00
|
|
|
let c_str = "hello".to_c_str();
|
|
|
|
let buf = c_str.as_ptr();
|
|
|
|
unsafe {
|
|
|
|
assert_eq!(*buf.offset(0), 'h' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(1), 'e' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(2), 'l' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(3), 'l' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(4), 'o' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(5), 0);
|
|
|
|
}
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
|
2013-09-15 22:30:03 -05:00
|
|
|
#[test]
|
|
|
|
fn test_vec_to_c_str() {
|
2014-11-17 02:39:01 -06:00
|
|
|
let b: &[u8] = &[];
|
2014-06-14 07:50:07 -05:00
|
|
|
let c_str = b.to_c_str();
|
|
|
|
unsafe {
|
|
|
|
assert_eq!(*c_str.as_ptr().offset(0), 0);
|
|
|
|
}
|
2013-09-15 22:30:03 -05:00
|
|
|
|
2014-06-14 07:50:07 -05:00
|
|
|
let c_str = b"hello".to_c_str();
|
|
|
|
let buf = c_str.as_ptr();
|
|
|
|
unsafe {
|
|
|
|
assert_eq!(*buf.offset(0), 'h' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(1), 'e' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(2), 'l' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(3), 'l' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(4), 'o' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(5), 0);
|
|
|
|
}
|
2013-09-15 22:30:03 -05:00
|
|
|
|
2014-06-14 07:50:07 -05:00
|
|
|
let c_str = b"foo\xFF".to_c_str();
|
|
|
|
let buf = c_str.as_ptr();
|
|
|
|
unsafe {
|
|
|
|
assert_eq!(*buf.offset(0), 'f' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(1), 'o' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(2), 'o' as libc::c_char);
|
2014-12-12 16:41:14 -06:00
|
|
|
assert_eq!(*buf.offset(3), 0xffu8 as libc::c_char);
|
2014-06-14 07:50:07 -05:00
|
|
|
assert_eq!(*buf.offset(4), 0);
|
|
|
|
}
|
2013-09-15 22:30:03 -05:00
|
|
|
}
|
|
|
|
|
2013-08-03 19:13:14 -05:00
|
|
|
#[test]
|
2013-08-04 20:37:55 -05:00
|
|
|
fn test_unwrap() {
|
|
|
|
let c_str = "hello".to_c_str();
|
2014-01-21 08:31:32 -06:00
|
|
|
unsafe { libc::free(c_str.unwrap() as *mut libc::c_void) }
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2014-06-14 07:50:07 -05:00
|
|
|
fn test_as_ptr() {
|
2013-08-03 19:13:14 -05:00
|
|
|
let c_str = "hello".to_c_str();
|
2014-06-14 07:50:07 -05:00
|
|
|
let len = unsafe { libc::strlen(c_str.as_ptr()) };
|
2013-08-03 19:13:14 -05:00
|
|
|
assert_eq!(len, 5);
|
|
|
|
}
|
2013-08-06 23:06:12 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_iterator() {
|
|
|
|
let c_str = "".to_c_str();
|
|
|
|
let mut iter = c_str.iter();
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
|
|
|
|
let c_str = "hello".to_c_str();
|
|
|
|
let mut iter = c_str.iter();
|
|
|
|
assert_eq!(iter.next(), Some('h' as libc::c_char));
|
|
|
|
assert_eq!(iter.next(), Some('e' as libc::c_char));
|
|
|
|
assert_eq!(iter.next(), Some('l' as libc::c_char));
|
|
|
|
assert_eq!(iter.next(), Some('l' as libc::c_char));
|
|
|
|
assert_eq!(iter.next(), Some('o' as libc::c_char));
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
2013-08-14 21:19:29 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_to_c_str_fail() {
|
2014-11-26 07:12:18 -06:00
|
|
|
assert!(task::try(move|| { "he\x00llo".to_c_str() }).is_err());
|
2013-08-14 21:19:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_to_c_str_unchecked() {
|
|
|
|
unsafe {
|
2014-06-14 07:50:07 -05:00
|
|
|
let c_string = "he\x00llo".to_c_str_unchecked();
|
|
|
|
let buf = c_string.as_ptr();
|
|
|
|
assert_eq!(*buf.offset(0), 'h' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(1), 'e' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(2), 0);
|
|
|
|
assert_eq!(*buf.offset(3), 'l' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(4), 'l' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(5), 'o' as libc::c_char);
|
|
|
|
assert_eq!(*buf.offset(6), 0);
|
2013-08-14 21:19:29 -05:00
|
|
|
}
|
|
|
|
}
|
2013-09-15 22:30:03 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_as_bytes() {
|
|
|
|
let c_str = "hello".to_c_str();
|
2014-06-18 13:25:36 -05:00
|
|
|
assert_eq!(c_str.as_bytes(), b"hello\0");
|
2013-09-15 22:30:03 -05:00
|
|
|
let c_str = "".to_c_str();
|
2014-06-18 13:25:36 -05:00
|
|
|
assert_eq!(c_str.as_bytes(), b"\0");
|
|
|
|
let c_str = b"foo\xFF".to_c_str();
|
|
|
|
assert_eq!(c_str.as_bytes(), b"foo\xFF\0");
|
2013-09-15 22:30:03 -05:00
|
|
|
}
|
|
|
|
|
2014-02-14 17:42:35 -06:00
|
|
|
#[test]
|
|
|
|
fn test_as_bytes_no_nul() {
|
|
|
|
let c_str = "hello".to_c_str();
|
2014-06-18 13:25:36 -05:00
|
|
|
assert_eq!(c_str.as_bytes_no_nul(), b"hello");
|
2014-02-14 17:42:35 -06:00
|
|
|
let c_str = "".to_c_str();
|
2014-11-17 02:39:01 -06:00
|
|
|
let exp: &[u8] = &[];
|
2014-02-14 17:42:35 -06:00
|
|
|
assert_eq!(c_str.as_bytes_no_nul(), exp);
|
2014-06-18 13:25:36 -05:00
|
|
|
let c_str = b"foo\xFF".to_c_str();
|
|
|
|
assert_eq!(c_str.as_bytes_no_nul(), b"foo\xFF");
|
2014-02-14 17:42:35 -06:00
|
|
|
}
|
|
|
|
|
2013-09-15 22:30:03 -05:00
|
|
|
#[test]
|
|
|
|
fn test_as_str() {
|
|
|
|
let c_str = "hello".to_c_str();
|
|
|
|
assert_eq!(c_str.as_str(), Some("hello"));
|
|
|
|
let c_str = "".to_c_str();
|
|
|
|
assert_eq!(c_str.as_str(), Some(""));
|
2014-06-18 13:25:36 -05:00
|
|
|
let c_str = b"foo\xFF".to_c_str();
|
2013-09-15 22:30:03 -05:00
|
|
|
assert_eq!(c_str.as_str(), None);
|
2014-01-30 17:29:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_fail]
|
2014-07-22 11:24:33 -05:00
|
|
|
fn test_new_fail() {
|
2014-09-09 04:32:58 -05:00
|
|
|
let _c_str = unsafe { CString::new(ptr::null(), false) };
|
2013-09-15 22:30:03 -05:00
|
|
|
}
|
2014-01-27 05:12:59 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_clone() {
|
2014-01-30 18:10:07 -06:00
|
|
|
let a = "hello".to_c_str();
|
|
|
|
let b = a.clone();
|
|
|
|
assert!(a == b);
|
2014-01-27 05:12:59 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_clone_noleak() {
|
2014-12-07 12:22:04 -06:00
|
|
|
fn foo<F>(f: F) where F: FnOnce(&CString) {
|
2014-05-25 05:10:11 -05:00
|
|
|
let s = "test".to_string();
|
2014-01-27 05:12:59 -06:00
|
|
|
let c = s.to_c_str();
|
|
|
|
// give the closure a non-owned CString
|
2014-06-14 07:50:07 -05:00
|
|
|
let mut c_ = unsafe { CString::new(c.as_ptr(), false) };
|
2014-01-27 05:12:59 -06:00
|
|
|
f(&c_);
|
|
|
|
// muck with the buffer for later printing
|
2014-06-14 07:50:07 -05:00
|
|
|
unsafe { *c_.as_mut_ptr() = 'X' as libc::c_char }
|
2014-01-27 05:12:59 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut c_: Option<CString> = None;
|
|
|
|
foo(|c| {
|
|
|
|
c_ = Some(c.clone());
|
|
|
|
c.clone();
|
|
|
|
// force a copy, reading the memory
|
2014-07-16 15:37:28 -05:00
|
|
|
c.as_bytes().to_vec();
|
2014-01-27 05:12:59 -06:00
|
|
|
});
|
|
|
|
let c_ = c_.unwrap();
|
|
|
|
// force a copy, reading the memory
|
2014-07-16 15:37:28 -05:00
|
|
|
c_.as_bytes().to_vec();
|
2014-01-27 05:12:59 -06:00
|
|
|
}
|
2013-08-03 19:13:14 -05:00
|
|
|
}
|
2013-09-18 14:32:45 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod bench {
|
2014-11-23 21:21:17 -06:00
|
|
|
extern crate test;
|
|
|
|
|
|
|
|
use self::test::Bencher;
|
2013-09-18 14:32:45 -05:00
|
|
|
use libc;
|
2014-11-23 21:21:17 -06:00
|
|
|
use prelude::*;
|
2013-09-18 14:32:45 -05:00
|
|
|
|
|
|
|
#[inline]
|
2014-06-25 14:47:34 -05:00
|
|
|
fn check(s: &str, c_str: *const libc::c_char) {
|
2013-12-17 09:37:30 -06:00
|
|
|
let s_buf = s.as_ptr();
|
|
|
|
for i in range(0, s.len()) {
|
|
|
|
unsafe {
|
|
|
|
assert_eq!(
|
2014-02-10 15:50:42 -06:00
|
|
|
*s_buf.offset(i as int) as libc::c_char,
|
|
|
|
*c_str.offset(i as int));
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
2013-12-17 09:37:30 -06:00
|
|
|
}
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
2014-09-12 20:55:37 -05:00
|
|
|
static S_SHORT: &'static str = "Mary";
|
|
|
|
static S_MEDIUM: &'static str = "Mary had a little lamb";
|
|
|
|
static S_LONG: &'static str = "\
|
2013-09-18 14:32:45 -05:00
|
|
|
Mary had a little lamb, Little lamb
|
|
|
|
Mary had a little lamb, Little lamb
|
|
|
|
Mary had a little lamb, Little lamb
|
|
|
|
Mary had a little lamb, Little lamb
|
|
|
|
Mary had a little lamb, Little lamb
|
|
|
|
Mary had a little lamb, Little lamb";
|
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
fn bench_to_string(b: &mut Bencher, s: &str) {
|
2014-03-31 20:16:35 -05:00
|
|
|
b.iter(|| {
|
2013-09-18 14:32:45 -05:00
|
|
|
let c_str = s.to_c_str();
|
2014-06-14 07:50:07 -05:00
|
|
|
check(s, c_str.as_ptr());
|
2013-11-20 16:17:12 -06:00
|
|
|
})
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_to_c_str_short(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_to_string(b, S_SHORT)
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_to_c_str_medium(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_to_string(b, S_MEDIUM)
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_to_c_str_long(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_to_string(b, S_LONG)
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_to_c_str_unchecked(b: &mut Bencher, s: &str) {
|
|
|
|
b.iter(|| {
|
2013-09-18 14:32:45 -05:00
|
|
|
let c_str = unsafe { s.to_c_str_unchecked() };
|
2014-06-14 07:50:07 -05:00
|
|
|
check(s, c_str.as_ptr())
|
2013-11-20 16:17:12 -06:00
|
|
|
})
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_to_c_str_unchecked_short(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_to_c_str_unchecked(b, S_SHORT)
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_to_c_str_unchecked_medium(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_to_c_str_unchecked(b, S_MEDIUM)
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_to_c_str_unchecked_long(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_to_c_str_unchecked(b, S_LONG)
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_with_c_str(b: &mut Bencher, s: &str) {
|
|
|
|
b.iter(|| {
|
2013-11-20 16:17:12 -06:00
|
|
|
s.with_c_str(|c_str_buf| check(s, c_str_buf))
|
|
|
|
})
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_with_c_str_short(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_with_c_str(b, S_SHORT)
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_with_c_str_medium(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_with_c_str(b, S_MEDIUM)
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_with_c_str_long(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_with_c_str(b, S_LONG)
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|
2013-09-20 18:48:07 -05:00
|
|
|
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_with_c_str_unchecked(b: &mut Bencher, s: &str) {
|
|
|
|
b.iter(|| {
|
2013-09-20 18:48:07 -05:00
|
|
|
unsafe {
|
2013-11-20 16:17:12 -06:00
|
|
|
s.with_c_str_unchecked(|c_str_buf| check(s, c_str_buf))
|
2013-09-20 18:48:07 -05:00
|
|
|
}
|
2013-11-20 16:17:12 -06:00
|
|
|
})
|
2013-09-20 18:48:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_with_c_str_unchecked_short(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_with_c_str_unchecked(b, S_SHORT)
|
2013-09-20 18:48:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_with_c_str_unchecked_medium(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_with_c_str_unchecked(b, S_MEDIUM)
|
2013-09-20 18:48:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
2014-03-31 20:16:35 -05:00
|
|
|
fn bench_with_c_str_unchecked_long(b: &mut Bencher) {
|
2014-09-12 20:55:37 -05:00
|
|
|
bench_with_c_str_unchecked(b, S_LONG)
|
2013-09-20 18:48:07 -05:00
|
|
|
}
|
2013-09-18 14:32:45 -05:00
|
|
|
}
|