Skip to content

Commit ec7a50d

Browse files
committed
std: Redesign c_str and c_vec
This commit is an implementation of [RFC 494][rfc] which removes the entire `std::c_vec` module and redesigns the `std::c_str` module as `std::ffi`. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0494-c_str-and-c_vec-stability.md The interface of the new `CString` is outlined in the linked RFC, the primary changes being: * The `ToCStr` trait is gone, meaning the `with_c_str` and `to_c_str` methods are now gone. These two methods are replaced with a `CString::from_slice` method. * The `CString` type is now just a wrapper around `Vec<u8>` with a static guarantee that there is a trailing nul byte with no internal nul bytes. This means that `CString` now implements `Deref<Target = [c_char]>`, which is where it gains most of its methods from. A few helper methods are added to acquire a slice of `u8` instead of `c_char`, as well as including a slice with the trailing nul byte if necessary. * All usage of non-owned `CString` values is now done via two functions inside of `std::ffi`, called `c_str_to_bytes` and `c_str_to_bytes_with_nul`. These functions are now the one method used to convert a `*const c_char` to a Rust slice of `u8`. Many more details, including newly deprecated methods, can be found linked in the RFC. This is a: [breaking-change] Closes #20444
1 parent 1f732ef commit ec7a50d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1023
-2003
lines changed

src/doc/guide-ffi.md

+27-23
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ them.
451451
~~~no_run
452452
extern crate libc;
453453
454-
use std::c_str::ToCStr;
454+
use std::ffi::CString;
455455
use std::ptr;
456456
457457
#[link(name = "readline")]
@@ -460,11 +460,10 @@ extern {
460460
}
461461
462462
fn main() {
463-
"[my-awesome-shell] $".with_c_str(|buf| {
464-
unsafe { rl_prompt = buf; }
465-
// get a line, process it
466-
unsafe { rl_prompt = ptr::null(); }
467-
});
463+
let prompt = CString::from_slice(b"[my-awesome-shell] $");
464+
unsafe { rl_prompt = prompt.as_ptr(); }
465+
// get a line, process it
466+
unsafe { rl_prompt = ptr::null(); }
468467
}
469468
~~~
470469
@@ -509,23 +508,28 @@ to define a block for all windows systems, not just x86 ones.
509508
510509
# Interoperability with foreign code
511510
512-
Rust guarantees that the layout of a `struct` is compatible with the platform's representation in C
513-
only if the `#[repr(C)]` attribute is applied to it. `#[repr(C, packed)]` can be used to lay out
514-
struct members without padding. `#[repr(C)]` can also be applied to an enum.
515-
516-
Rust's owned boxes (`Box<T>`) use non-nullable pointers as handles which point to the contained
517-
object. However, they should not be manually created because they are managed by internal
518-
allocators. References can safely be assumed to be non-nullable pointers directly to the type.
519-
However, breaking the borrow checking or mutability rules is not guaranteed to be safe, so prefer
520-
using raw pointers (`*`) if that's needed because the compiler can't make as many assumptions about
521-
them.
522-
523-
Vectors and strings share the same basic memory layout, and utilities are available in the `vec` and
524-
`str` modules for working with C APIs. However, strings are not terminated with `\0`. If you need a
525-
NUL-terminated string for interoperability with C, you should use the `c_str::to_c_str` function.
526-
527-
The standard library includes type aliases and function definitions for the C standard library in
528-
the `libc` module, and Rust links against `libc` and `libm` by default.
511+
Rust guarantees that the layout of a `struct` is compatible with the platform's
512+
representation in C only if the `#[repr(C)]` attribute is applied to it.
513+
`#[repr(C, packed)]` can be used to lay out struct members without padding.
514+
`#[repr(C)]` can also be applied to an enum.
515+
516+
Rust's owned boxes (`Box<T>`) use non-nullable pointers as handles which point
517+
to the contained object. However, they should not be manually created because
518+
they are managed by internal allocators. References can safely be assumed to be
519+
non-nullable pointers directly to the type. However, breaking the borrow
520+
checking or mutability rules is not guaranteed to be safe, so prefer using raw
521+
pointers (`*`) if that's needed because the compiler can't make as many
522+
assumptions about them.
523+
524+
Vectors and strings share the same basic memory layout, and utilities are
525+
available in the `vec` and `str` modules for working with C APIs. However,
526+
strings are not terminated with `\0`. If you need a NUL-terminated string for
527+
interoperability with C, you should use the `CString` type in the `std::ffi`
528+
module.
529+
530+
The standard library includes type aliases and function definitions for the C
531+
standard library in the `libc` module, and Rust links against `libc` and `libm`
532+
by default.
529533
530534
# The "nullable pointer optimization"
531535

src/libcollections/string.rs

-42
Original file line numberDiff line numberDiff line change
@@ -320,30 +320,6 @@ impl String {
320320
}
321321
}
322322

323-
/// Creates a `String` from a null-terminated `*const u8` buffer.
324-
///
325-
/// This function is unsafe because we dereference memory until we find the
326-
/// NUL character, which is not guaranteed to be present. Additionally, the
327-
/// slice is not checked to see whether it contains valid UTF-8
328-
#[unstable = "just renamed from `mod raw`"]
329-
pub unsafe fn from_raw_buf(buf: *const u8) -> String {
330-
String::from_str(str::from_c_str(buf as *const i8))
331-
}
332-
333-
/// Creates a `String` from a `*const u8` buffer of the given length.
334-
///
335-
/// This function is unsafe because it blindly assumes the validity of the
336-
/// pointer `buf` for `len` bytes of memory. This function will copy the
337-
/// memory from `buf` into a new allocation (owned by the returned
338-
/// `String`).
339-
///
340-
/// This function is also unsafe because it does not validate that the
341-
/// buffer is valid UTF-8 encoded data.
342-
#[unstable = "just renamed from `mod raw`"]
343-
pub unsafe fn from_raw_buf_len(buf: *const u8, len: uint) -> String {
344-
String::from_utf8_unchecked(Vec::from_raw_buf(buf, len))
345-
}
346-
347323
/// Converts a vector of bytes to a new `String` without checking if
348324
/// it contains valid UTF-8. This is unsafe because it assumes that
349325
/// the UTF-8-ness of the vector has already been validated.
@@ -1126,24 +1102,6 @@ mod tests {
11261102
String::from_str("\u{FFFD}𐒋\u{FFFD}"));
11271103
}
11281104

1129-
#[test]
1130-
fn test_from_buf_len() {
1131-
unsafe {
1132-
let a = vec![65u8, 65, 65, 65, 65, 65, 65, 0];
1133-
assert_eq!(String::from_raw_buf_len(a.as_ptr(), 3), String::from_str("AAA"));
1134-
}
1135-
}
1136-
1137-
#[test]
1138-
fn test_from_buf() {
1139-
unsafe {
1140-
let a = vec![65, 65, 65, 65, 65, 65, 65, 0];
1141-
let b = a.as_ptr();
1142-
let c = String::from_raw_buf(b);
1143-
assert_eq!(c, String::from_str("AAAAAAA"));
1144-
}
1145-
}
1146-
11471105
#[test]
11481106
fn test_push_bytes() {
11491107
let mut s = String::from_str("ABC");

src/libcore/str/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ pub unsafe fn from_utf8_unchecked<'a>(v: &'a [u8]) -> &'a str {
190190
/// # Panics
191191
///
192192
/// This function will panic if the string pointed to by `s` is not valid UTF-8.
193-
#[unstable = "may change location based on the outcome of the c_str module"]
193+
#[deprecated = "use std::ffi::c_str_to_bytes + str::from_utf8"]
194194
pub unsafe fn from_c_str(s: *const i8) -> &'static str {
195195
let s = s as *const u8;
196196
let mut len = 0u;

src/libflate/lib.rs

+31-12
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,34 @@
2121
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
2222
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
2323
html_root_url = "http://doc.rust-lang.org/nightly/")]
24-
#![feature(phase, unboxed_closures)]
24+
#![feature(phase, unboxed_closures, associated_types)]
2525

2626
#[cfg(test)] #[phase(plugin, link)] extern crate log;
2727

2828
extern crate libc;
2929

3030
use libc::{c_void, size_t, c_int};
31-
use std::c_vec::CVec;
31+
use std::ops::Deref;
3232
use std::ptr::Unique;
33+
use std::slice;
34+
35+
pub struct Bytes {
36+
ptr: Unique<u8>,
37+
len: uint,
38+
}
39+
40+
impl Deref for Bytes {
41+
type Target = [u8];
42+
fn deref(&self) -> &[u8] {
43+
unsafe { slice::from_raw_mut_buf(&self.ptr.0, self.len) }
44+
}
45+
}
46+
47+
impl Drop for Bytes {
48+
fn drop(&mut self) {
49+
unsafe { libc::free(self.ptr.0 as *mut _); }
50+
}
51+
}
3352

3453
#[link(name = "miniz", kind = "static")]
3554
extern {
@@ -52,55 +71,55 @@ static LZ_NORM : c_int = 0x80; // LZ with 128 probes, "normal"
5271
static TINFL_FLAG_PARSE_ZLIB_HEADER : c_int = 0x1; // parse zlib header and adler32 checksum
5372
static TDEFL_WRITE_ZLIB_HEADER : c_int = 0x01000; // write zlib header and adler32 checksum
5473

55-
fn deflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
74+
fn deflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<Bytes> {
5675
unsafe {
5776
let mut outsz : size_t = 0;
5877
let res = tdefl_compress_mem_to_heap(bytes.as_ptr() as *const _,
5978
bytes.len() as size_t,
6079
&mut outsz,
6180
flags);
6281
if !res.is_null() {
63-
let res = Unique(res);
64-
Some(CVec::new_with_dtor(res.0 as *mut u8, outsz as uint, move|:| libc::free(res.0)))
82+
let res = Unique(res as *mut u8);
83+
Some(Bytes { ptr: res, len: outsz as uint })
6584
} else {
6685
None
6786
}
6887
}
6988
}
7089

7190
/// Compress a buffer, without writing any sort of header on the output.
72-
pub fn deflate_bytes(bytes: &[u8]) -> Option<CVec<u8>> {
91+
pub fn deflate_bytes(bytes: &[u8]) -> Option<Bytes> {
7392
deflate_bytes_internal(bytes, LZ_NORM)
7493
}
7594

7695
/// Compress a buffer, using a header that zlib can understand.
77-
pub fn deflate_bytes_zlib(bytes: &[u8]) -> Option<CVec<u8>> {
96+
pub fn deflate_bytes_zlib(bytes: &[u8]) -> Option<Bytes> {
7897
deflate_bytes_internal(bytes, LZ_NORM | TDEFL_WRITE_ZLIB_HEADER)
7998
}
8099

81-
fn inflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
100+
fn inflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<Bytes> {
82101
unsafe {
83102
let mut outsz : size_t = 0;
84103
let res = tinfl_decompress_mem_to_heap(bytes.as_ptr() as *const _,
85104
bytes.len() as size_t,
86105
&mut outsz,
87106
flags);
88107
if !res.is_null() {
89-
let res = Unique(res);
90-
Some(CVec::new_with_dtor(res.0 as *mut u8, outsz as uint, move|:| libc::free(res.0)))
108+
let res = Unique(res as *mut u8);
109+
Some(Bytes { ptr: res, len: outsz as uint })
91110
} else {
92111
None
93112
}
94113
}
95114
}
96115

97116
/// Decompress a buffer, without parsing any sort of header on the input.
98-
pub fn inflate_bytes(bytes: &[u8]) -> Option<CVec<u8>> {
117+
pub fn inflate_bytes(bytes: &[u8]) -> Option<Bytes> {
99118
inflate_bytes_internal(bytes, 0)
100119
}
101120

102121
/// Decompress a buffer that starts with a zlib header.
103-
pub fn inflate_bytes_zlib(bytes: &[u8]) -> Option<CVec<u8>> {
122+
pub fn inflate_bytes_zlib(bytes: &[u8]) -> Option<Bytes> {
104123
inflate_bytes_internal(bytes, TINFL_FLAG_PARSE_ZLIB_HEADER)
105124
}
106125

src/librustc/metadata/cstore.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ use metadata::loader;
2323
use util::nodemap::{FnvHashMap, NodeMap};
2424

2525
use std::cell::RefCell;
26-
use std::c_vec::CVec;
2726
use std::rc::Rc;
27+
use flate::Bytes;
2828
use syntax::ast;
2929
use syntax::codemap::Span;
3030
use syntax::parse::token::IdentInterner;
@@ -36,7 +36,7 @@ use syntax::parse::token::IdentInterner;
3636
pub type cnum_map = FnvHashMap<ast::CrateNum, ast::CrateNum>;
3737

3838
pub enum MetadataBlob {
39-
MetadataVec(CVec<u8>),
39+
MetadataVec(Bytes),
4040
MetadataArchive(loader::ArchiveMetadata),
4141
}
4242

src/librustc/metadata/loader.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ use syntax::codemap::Span;
226226
use syntax::diagnostic::SpanHandler;
227227
use util::fs;
228228

229-
use std::c_str::ToCStr;
229+
use std::ffi::CString;
230230
use std::cmp;
231231
use std::collections::{HashMap, HashSet};
232232
use std::io::fs::PathExtensions;
@@ -720,9 +720,8 @@ fn get_metadata_section_imp(is_osx: bool, filename: &Path) -> Result<MetadataBlo
720720
}
721721
}
722722
unsafe {
723-
let mb = filename.with_c_str(|buf| {
724-
llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
725-
});
723+
let buf = CString::from_slice(filename.as_vec());
724+
let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr());
726725
if mb as int == 0 {
727726
return Err(format!("error reading library: '{}'",
728727
filename.display()))
@@ -738,8 +737,9 @@ fn get_metadata_section_imp(is_osx: bool, filename: &Path) -> Result<MetadataBlo
738737
while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
739738
let mut name_buf = ptr::null();
740739
let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf);
741-
let name = String::from_raw_buf_len(name_buf as *const u8,
742-
name_len as uint);
740+
let name = slice::from_raw_buf(&(name_buf as *const u8),
741+
name_len as uint).to_vec();
742+
let name = String::from_utf8(name).unwrap();
743743
debug!("get_metadata_section: name {}", name);
744744
if read_meta_section_name(is_osx) == name {
745745
let cbuf = llvm::LLVMGetSectionContents(si.llsi);

src/librustc_llvm/archive_ro.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use libc;
1414
use ArchiveRef;
1515

16-
use std::c_str::ToCStr;
16+
use std::ffi::CString;
1717
use std::mem;
1818
use std::raw;
1919

@@ -30,9 +30,8 @@ impl ArchiveRO {
3030
/// raised.
3131
pub fn open(dst: &Path) -> Option<ArchiveRO> {
3232
unsafe {
33-
let ar = dst.with_c_str(|dst| {
34-
::LLVMRustOpenArchive(dst)
35-
});
33+
let s = CString::from_slice(dst.as_vec());
34+
let ar = ::LLVMRustOpenArchive(s.as_ptr());
3635
if ar.is_null() {
3736
None
3837
} else {
@@ -45,9 +44,9 @@ impl ArchiveRO {
4544
pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
4645
unsafe {
4746
let mut size = 0 as libc::size_t;
48-
let ptr = file.with_c_str(|file| {
49-
::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
50-
});
47+
let file = CString::from_slice(file.as_bytes());
48+
let ptr = ::LLVMRustArchiveReadSection(self.ptr, file.as_ptr(),
49+
&mut size);
5150
if ptr.is_null() {
5251
None
5352
} else {

src/librustc_llvm/lib.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub use self::Visibility::*;
4747
pub use self::DiagnosticSeverity::*;
4848
pub use self::Linkage::*;
4949

50-
use std::c_str::ToCStr;
50+
use std::ffi::CString;
5151
use std::cell::RefCell;
5252
use std::{raw, mem};
5353
use libc::{c_uint, c_ushort, uint64_t, c_int, size_t, c_char};
@@ -2114,10 +2114,9 @@ impl Drop for TargetData {
21142114
}
21152115

21162116
pub fn mk_target_data(string_rep: &str) -> TargetData {
2117+
let string_rep = CString::from_slice(string_rep.as_bytes());
21172118
TargetData {
2118-
lltd: string_rep.with_c_str(|buf| {
2119-
unsafe { LLVMCreateTargetData(buf) }
2120-
})
2119+
lltd: unsafe { LLVMCreateTargetData(string_rep.as_ptr()) }
21212120
}
21222121
}
21232122

src/librustc_trans/back/lto.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc::util::common::time;
2020
use libc;
2121
use flate;
2222

23-
use std::c_str::ToCStr;
23+
use std::ffi::CString;
2424
use std::iter;
2525
use std::mem;
2626
use std::num::Int;
@@ -139,9 +139,10 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
139139
}
140140

141141
// Internalize everything but the reachable symbols of the current module
142-
let cstrs: Vec<::std::c_str::CString> =
143-
reachable.iter().map(|s| s.to_c_str()).collect();
144-
let arr: Vec<*const libc::c_char> = cstrs.iter().map(|c| c.as_ptr()).collect();
142+
let cstrs: Vec<CString> = reachable.iter().map(|s| {
143+
CString::from_slice(s.as_bytes())
144+
}).collect();
145+
let arr: Vec<*const i8> = cstrs.iter().map(|c| c.as_ptr()).collect();
145146
let ptr = arr.as_ptr();
146147
unsafe {
147148
llvm::LLVMRustRunRestrictionPass(llmod,
@@ -164,15 +165,15 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
164165
unsafe {
165166
let pm = llvm::LLVMCreatePassManager();
166167
llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod);
167-
"verify".with_c_str(|s| llvm::LLVMRustAddPass(pm, s));
168+
llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _);
168169

169170
let builder = llvm::LLVMPassManagerBuilderCreate();
170171
llvm::LLVMPassManagerBuilderPopulateLTOPassManager(builder, pm,
171172
/* Internalize = */ False,
172173
/* RunInliner = */ True);
173174
llvm::LLVMPassManagerBuilderDispose(builder);
174175

175-
"verify".with_c_str(|s| llvm::LLVMRustAddPass(pm, s));
176+
llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _);
176177

177178
time(sess.time_passes(), "LTO passes", (), |()|
178179
llvm::LLVMRunPassManager(pm, llmod));

0 commit comments

Comments
 (0)