From 6011f83dec61a33174652e4c76a463dd32626de8 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sat, 3 Aug 2013 12:26:44 -0700 Subject: [PATCH 01/26] std: minor cleanup --- src/libstd/rt/borrowck.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstd/rt/borrowck.rs b/src/libstd/rt/borrowck.rs index bb66a90123428..4a6a9585b9374 100644 --- a/src/libstd/rt/borrowck.rs +++ b/src/libstd/rt/borrowck.rs @@ -77,7 +77,7 @@ unsafe fn fail_borrowed(box: *mut raw::Box<()>, file: *c_char, line: size_t) { None => { // not recording borrows let msg = "borrowed"; do msg.as_c_str |msg_p| { - sys::begin_unwind_(msg_p as *c_char, file, line); + sys::begin_unwind_(msg_p, file, line); } } Some(borrow_list) => { // recording borrows @@ -93,7 +93,7 @@ unsafe fn fail_borrowed(box: *mut raw::Box<()>, file: *c_char, line: size_t) { } } do msg.as_c_str |msg_p| { - sys::begin_unwind_(msg_p as *c_char, file, line) + sys::begin_unwind_(msg_p, file, line) } } } @@ -232,7 +232,7 @@ pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint, if br.box != a || br.file != file || br.line != line { let err = fmt!("wrong borrow found, br=%?", br); do err.as_c_str |msg_p| { - sys::begin_unwind_(msg_p as *c_char, file, line) + sys::begin_unwind_(msg_p, file, line) } } borrow_list From 08b6cb46c6c5cc583ceccb2c29e65fc72c13f480 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sat, 3 Aug 2013 14:13:34 -0700 Subject: [PATCH 02/26] std: add str.to_c_str() --- src/libstd/os.rs | 2 +- src/libstd/str.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index f78f581286337..294fa36132480 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -1948,7 +1948,7 @@ mod tests { }; assert!((ostream as uint != 0u)); let s = ~"hello"; - let mut buf = s.as_bytes_with_null().to_owned(); + let mut buf = s.to_owned().to_c_str(); let len = buf.len(); do buf.as_mut_buf |b, _len| { assert_eq!(libc::fwrite(b as *c_void, 1u as size_t, diff --git a/src/libstd/str.rs b/src/libstd/str.rs index c30888529be10..c936c1e25db60 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -2024,6 +2024,13 @@ pub trait OwnedStr { fn capacity(&self) -> uint; fn to_bytes_with_null(self) -> ~[u8]; + /// Allocates a null terminate byte array. + /// + /// # Failure + /// + /// Fails if there are any null characters inside the byte array. + fn to_c_str(self) -> ~[u8]; + /// Work with the mutable byte buffer and length of a slice. /// /// The given length is one byte longer than the 'official' indexable @@ -2215,6 +2222,13 @@ impl OwnedStr for ~str { unsafe { cast::transmute(self) } } + #[inline] + fn to_c_str(self) -> ~[u8] { + let bytes = self.to_bytes_with_null(); + assert!(bytes.slice(0, bytes.len() - 1).iter().all(|byte| *byte != 0)); + bytes + } + #[inline] fn as_mut_buf(&mut self, f: &fn(*mut u8, uint) -> T) -> T { let v: &mut ~[u8] = unsafe { cast::transmute(self) }; @@ -3059,6 +3073,18 @@ mod tests { } #[test] + fn test_to_c_str() { + let s = ~"ศไทย中华Việt Nam"; + let v = ~[ + 224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228, + 184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97, + 109, 0 + ]; + assert_eq!((~"").to_c_str(), ~[0]); + assert_eq!((~"abc").to_c_str(), ~['a' as u8, 'b' as u8, 'c' as u8, 0]); + assert_eq!(s.to_c_str(), v); + } + fn test_subslice_offset() { let a = "kernelsprite"; let b = a.slice(7, a.len()); From fd293dfb0f97962697a967b2fae12b54225d7a11 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sat, 29 Jun 2013 18:45:54 -0700 Subject: [PATCH 03/26] std: rewrite run::with_{argv,envp,dirp} to copy C strings --- src/libstd/run.rs | 103 ++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 44 deletions(-) diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 4c67d844c7e81..9114c4879b245 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -24,6 +24,7 @@ use prelude::*; use ptr; use task; use vec::ImmutableVector; +use vec; /** * A value representing a child process. @@ -690,46 +691,58 @@ fn spawn_process_os(prog: &str, args: &[~str], } #[cfg(unix)] -fn with_argv(prog: &str, args: &[~str], - cb: &fn(**libc::c_char) -> T) -> T { - let mut argptrs = ~[prog.as_c_str(|b| b)]; - let mut tmps = ~[]; +fn with_argv(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T { + // We can't directly convert `str`s into `*char`s, as someone needs to hold + // a reference to the intermediary byte buffers. So first build an array to + // hold all the ~[u8] byte strings. + let mut tmps = vec::with_capacity(args.len() + 1); + + tmps.push(prog.to_owned().to_c_str()); + foreach arg in args.iter() { - let t = @(*arg).clone(); - tmps.push(t); - argptrs.push(t.as_c_str(|b| b)); + tmps.push(arg.to_owned().to_c_str()); } - argptrs.push(ptr::null()); - argptrs.as_imm_buf(|buf, _len| cb(buf)) + + // Next, convert each of the byte strings into a pointer. This is + // technically unsafe as the caller could leak these pointers out of our + // scope. + let mut ptrs = do tmps.map |tmp| { + tmp.as_imm_buf(|buf, _| buf as *libc::c_char) + }; + + // Finally, make sure we add a null pointer. + ptrs.push(ptr::null()); + + ptrs.as_imm_buf(|buf, _| cb(buf)) } #[cfg(unix)] fn with_envp(env: Option<&[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T { - // On posixy systems we can pass a char** for envp, which is - // a null-terminated array of "k=v\n" strings. + // On posixy systems we can pass a char** for envp, which is a + // null-terminated array of "k=v\n" strings. Like `with_argv`, we have to + // have a temporary buffer to hold the intermediary `~[u8]` byte strings. match env { - Some(es) => { - let mut tmps = ~[]; - let mut ptrs = ~[]; - - foreach pair in es.iter() { - // Use of match here is just to workaround limitations - // in the stage0 irrefutable pattern impl. - match pair { - &(ref k, ref v) => { - let kv = @fmt!("%s=%s", *k, *v); - tmps.push(kv); - ptrs.push(kv.as_c_str(|b| b)); - } + Some(env) => { + let mut tmps = vec::with_capacity(env.len()); + + foreach pair in env.iter() { + // Use of match here is just to workaround limitations + // in the stage0 irrefutable pattern impl. + let kv = fmt!("%s=%s", pair.first(), pair.second()); + tmps.push(kv.to_c_str()); } - } - ptrs.push(ptr::null()); - ptrs.as_imm_buf(|p, _len| - unsafe { cb(::cast::transmute(p)) } - ) - } - _ => cb(ptr::null()) + // Once again, this is unsafe. + let mut ptrs = do tmps.map |tmp| { + tmp.as_imm_buf(|buf, _| buf as *libc::c_char) + }; + ptrs.push(ptr::null()); + + do ptrs.as_imm_buf |buf, _| { + unsafe { cb(cast::transmute(buf)) } + } + } + _ => cb(ptr::null()) } } @@ -739,23 +752,25 @@ fn with_envp(env: Option<&[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T { // rather a concatenation of null-terminated k=v\0 sequences, with a final // \0 to terminate. match env { - Some(es) => { - let mut blk = ~[]; - foreach pair in es.iter() { - let kv = fmt!("%s=%s", pair.first(), pair.second()); - blk.push_all(kv.to_bytes_with_null()); + Some(env) => { + let mut blk = ~[]; + + foreach pair in env.iter() { + let kv = fmt!("%s=%s", pair.first(), pair.second()); + blk.push_all(kv.to_c_str()); + } + + blk.push(0); + + do blk.as_imm_buf |p, _len| { + unsafe { cb(cast::transmute(p)) } + } } - blk.push(0); - blk.as_imm_buf(|p, _len| - unsafe { cb(::cast::transmute(p)) } - ) - } - _ => cb(ptr::mut_null()) + _ => cb(ptr::mut_null()) } } -fn with_dirp(d: Option<&Path>, - cb: &fn(*libc::c_char) -> T) -> T { +fn with_dirp(d: Option<&Path>, cb: &fn(*libc::c_char) -> T) -> T { match d { Some(dir) => dir.to_str().as_c_str(cb), None => cb(ptr::null()) From dca9ff9a13b0ca04160828413e4550225fb1e04f Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Wed, 3 Jul 2013 20:02:09 -0700 Subject: [PATCH 04/26] std: remove str::NullTerminatedStr --- src/libstd/prelude.rs | 2 +- src/libstd/str.rs | 55 +------------------ .../static-slice-not-null-terminated.rs | 21 ------- 3 files changed, 2 insertions(+), 76 deletions(-) delete mode 100644 src/test/compile-fail/static-slice-not-null-terminated.rs diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 517bc4a441a68..7e21504c1d294 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -63,7 +63,7 @@ pub use path::PosixPath; pub use path::WindowsPath; pub use ptr::RawPtr; pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr, ToBytesConsume}; -pub use str::{Str, StrVector, StrSlice, OwnedStr, NullTerminatedStr}; +pub use str::{Str, StrVector, StrSlice, OwnedStr}; pub use from_str::{FromStr}; pub use to_bytes::IterBytes; pub use to_str::{ToStr, ToStrConsume}; diff --git a/src/libstd/str.rs b/src/libstd/str.rs index c936c1e25db60..2a24351fff4e2 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -1981,35 +1981,6 @@ impl<'self> StrSlice<'self> for &'self str { } } -#[allow(missing_doc)] -pub trait NullTerminatedStr { - fn as_bytes_with_null<'a>(&'a self) -> &'a [u8]; -} - -impl NullTerminatedStr for ~str { - /// Work with the byte buffer of a string as a byte slice. - /// - /// The byte slice does include the null terminator. - #[inline] - fn as_bytes_with_null<'a>(&'a self) -> &'a [u8] { - let ptr: &'a ~[u8] = unsafe { cast::transmute(self) }; - let slice: &'a [u8] = *ptr; - slice - } -} - -impl NullTerminatedStr for @str { - /// Work with the byte buffer of a string as a byte slice. - /// - /// The byte slice does include the null terminator. - #[inline] - fn as_bytes_with_null<'a>(&'a self) -> &'a [u8] { - let ptr: &'a @[u8] = unsafe { cast::transmute(self) }; - let slice: &'a [u8] = *ptr; - slice - } -} - #[allow(missing_doc)] pub trait OwnedStr { fn push_str_no_overallocate(&mut self, rhs: &str); @@ -2979,30 +2950,6 @@ mod tests { assert_eq!("ศไทย中华Việt Nam".as_bytes(), v); } - #[test] - fn test_as_bytes_with_null() { - // has null - let v = [ - 224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228, - 184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97, - 109, 0 - ]; - - let s1 = @""; - let s2 = @"abc"; - let s3 = @"ศไทย中华Việt Nam"; - assert_eq!(s1.as_bytes_with_null(), &[0]); - assert_eq!(s2.as_bytes_with_null(), &['a' as u8, 'b' as u8, 'c' as u8, 0]); - assert_eq!(s3.as_bytes_with_null(), v); - - let s1 = ~""; - let s2 = ~"abc"; - let s3 = ~"ศไทย中华Việt Nam"; - assert_eq!(s1.as_bytes_with_null(), &[0]); - assert_eq!(s2.as_bytes_with_null(), &['a' as u8, 'b' as u8, 'c' as u8, 0]); - assert_eq!(s3.as_bytes_with_null(), v); - } - #[test] fn test_to_bytes_with_null() { let s = ~"ศไทย中华Việt Nam"; @@ -3024,7 +2971,7 @@ mod tests { // Don't double free. (I'm not sure if this exercises the // original problem code path anymore.) let s = ~""; - let _bytes = s.as_bytes_with_null(); + let _bytes = s.as_bytes(); fail!(); } diff --git a/src/test/compile-fail/static-slice-not-null-terminated.rs b/src/test/compile-fail/static-slice-not-null-terminated.rs deleted file mode 100644 index 3cfaa57d540f3..0000000000000 --- a/src/test/compile-fail/static-slice-not-null-terminated.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn main() { - let _ = (~"foo").as_bytes_with_null(); - let _ = (@"foo").as_bytes_with_null(); - - // a plain static slice is null terminated, but such a slice can - // be sliced shorter (i.e. become non-null terminated) and still - // have the static lifetime - let foo: &'static str = "foo"; - let _ = foo.as_bytes_with_null(); - //~^ ERROR does not implement any method in scope named `as_bytes_with_null` -} From cd94e9121b082370a5b85d6ba431c04b79db7e10 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 7 Jul 2013 13:13:58 -0700 Subject: [PATCH 05/26] std: cleanup os and str tests --- src/libstd/os.rs | 56 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 294fa36132480..f17a59db38f5a 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -1934,39 +1934,39 @@ mod tests { #[test] fn copy_file_ok() { unsafe { - let tempdir = getcwd(); // would like to use $TMPDIR, - // doesn't seem to work on Linux - assert!((tempdir.to_str().len() > 0u)); - let input = tempdir.push("in.txt"); - let out = tempdir.push("out.txt"); + let tempdir = getcwd(); // would like to use $TMPDIR, + // doesn't seem to work on Linux + assert!((tempdir.to_str().len() > 0u)); + let input = tempdir.push("in.txt"); + let out = tempdir.push("out.txt"); - /* Write the temp input file */ + /* Write the temp input file */ let ostream = do input.to_str().as_c_str |fromp| { do "w+b".as_c_str |modebuf| { libc::fopen(fromp, modebuf) } - }; - assert!((ostream as uint != 0u)); - let s = ~"hello"; - let mut buf = s.to_owned().to_c_str(); - let len = buf.len(); - do buf.as_mut_buf |b, _len| { - assert_eq!(libc::fwrite(b as *c_void, 1u as size_t, - (s.len() + 1u) as size_t, ostream), - len as size_t) - } - assert_eq!(libc::fclose(ostream), (0u as c_int)); - let in_mode = input.get_mode(); - let rs = os::copy_file(&input, &out); - if (!os::path_exists(&input)) { - fail!("%s doesn't exist", input.to_str()); - } - assert!((rs)); - let rslt = run::process_status("diff", [input.to_str(), out.to_str()]); - assert_eq!(rslt, 0); - assert_eq!(out.get_mode(), in_mode); - assert!((remove_file(&input))); - assert!((remove_file(&out))); + }; + assert!((ostream as uint != 0u)); + let s = ~"hello"; + let mut buf = s.to_owned().to_c_str(); + let len = buf.len(); + do buf.as_mut_buf |b, _len| { + assert_eq!(libc::fwrite(b as *c_void, 1u as size_t, + (s.len() + 1u) as size_t, ostream), + len as size_t) + } + assert_eq!(libc::fclose(ostream), (0u as c_int)); + let in_mode = input.get_mode(); + let rs = os::copy_file(&input, &out); + if (!os::path_exists(&input)) { + fail!("%s doesn't exist", input.to_str()); + } + assert!((rs)); + let rslt = run::process_status("diff", [input.to_str(), out.to_str()]); + assert_eq!(rslt, 0); + assert_eq!(out.get_mode(), in_mode); + assert!((remove_file(&input))); + assert!((remove_file(&out))); } } From d5110854f76eac307e7a1729f3003e09d0277b60 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 30 Jun 2013 08:29:38 -0700 Subject: [PATCH 06/26] std: add test for str::as_c_str --- src/libstd/str.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 2a24351fff4e2..e520c4d247662 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -3032,6 +3032,29 @@ mod tests { assert_eq!(s.to_c_str(), v); } + #[test] + fn test_as_c_str() { + let a = ~""; + do a.as_c_str |buf| { + unsafe { + assert_eq!(*ptr::offset(buf, 0), 0); + } + } + + let a = ~"hello"; + do a.as_c_str |buf| { + unsafe { + assert_eq!(*ptr::offset(buf, 0), 'h' as libc::c_char); + assert_eq!(*ptr::offset(buf, 1), 'e' as libc::c_char); + assert_eq!(*ptr::offset(buf, 2), 'l' as libc::c_char); + assert_eq!(*ptr::offset(buf, 3), 'l' as libc::c_char); + assert_eq!(*ptr::offset(buf, 4), 'o' as libc::c_char); + assert_eq!(*ptr::offset(buf, 5), 0); + } + } + } + + #[test] fn test_subslice_offset() { let a = "kernelsprite"; let b = a.slice(7, a.len()); From bb5bf7c3e0f82328e5be5235a92d20bf01bc9915 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 4 Jul 2013 16:21:35 -0700 Subject: [PATCH 07/26] std: remove str::from_bytes_with_null --- src/libstd/str.rs | 82 ----------------------------------------------- 1 file changed, 82 deletions(-) diff --git a/src/libstd/str.rs b/src/libstd/str.rs index e520c4d247662..f75bbcf20ebcb 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -83,21 +83,6 @@ pub fn from_bytes_owned(vv: ~[u8]) -> ~str { } } -/// Convert a vector of bytes to a UTF-8 string. -/// The vector needs to be one byte longer than the string, and end with a 0 byte. -/// -/// Compared to `from_bytes()`, this fn doesn't need to allocate a new owned str. -/// -/// # Failure -/// -/// Fails if invalid UTF-8 -/// Fails if not null terminated -pub fn from_bytes_with_null<'a>(vv: &'a [u8]) -> &'a str { - assert_eq!(vv[vv.len() - 1], 0); - assert!(is_utf8(vv)); - return unsafe { raw::from_bytes_with_null(vv) }; -} - /// Converts a vector to a string slice without performing any allocations. /// /// Once the slice has been validated as utf-8, it is transmuted in-place and @@ -826,13 +811,6 @@ pub mod raw { cast::transmute(v) } - /// Converts a vector of bytes to a string. - /// The byte slice needs to contain valid utf8 and needs to be one byte longer than - /// the string, if possible ending in a 0 byte. - pub unsafe fn from_bytes_with_null<'a>(v: &'a [u8]) -> &'a str { - cast::transmute(v) - } - /// Converts a byte to a string. pub unsafe fn from_byte(u: u8) -> ~str { from_bytes([u]) } @@ -2867,66 +2845,6 @@ mod tests { assert!(error_happened); } - #[test] - fn test_unsafe_from_bytes_with_null() { - let a = [65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 0u8]; - let b = unsafe { raw::from_bytes_with_null(a) }; - assert_eq!(b, "AAAAAAA"); - } - - #[test] - fn test_from_bytes_with_null() { - let ss = "ศไทย中华Việt Nam"; - let bb = [0xe0_u8, 0xb8_u8, 0xa8_u8, - 0xe0_u8, 0xb9_u8, 0x84_u8, - 0xe0_u8, 0xb8_u8, 0x97_u8, - 0xe0_u8, 0xb8_u8, 0xa2_u8, - 0xe4_u8, 0xb8_u8, 0xad_u8, - 0xe5_u8, 0x8d_u8, 0x8e_u8, - 0x56_u8, 0x69_u8, 0xe1_u8, - 0xbb_u8, 0x87_u8, 0x74_u8, - 0x20_u8, 0x4e_u8, 0x61_u8, - 0x6d_u8, 0x0_u8]; - - assert_eq!(ss, from_bytes_with_null(bb)); - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_from_bytes_with_null_fail() { - let bb = [0xff_u8, 0xb8_u8, 0xa8_u8, - 0xe0_u8, 0xb9_u8, 0x84_u8, - 0xe0_u8, 0xb8_u8, 0x97_u8, - 0xe0_u8, 0xb8_u8, 0xa2_u8, - 0xe4_u8, 0xb8_u8, 0xad_u8, - 0xe5_u8, 0x8d_u8, 0x8e_u8, - 0x56_u8, 0x69_u8, 0xe1_u8, - 0xbb_u8, 0x87_u8, 0x74_u8, - 0x20_u8, 0x4e_u8, 0x61_u8, - 0x6d_u8, 0x0_u8]; - - let _x = from_bytes_with_null(bb); - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_from_bytes_with_null_fail_2() { - let bb = [0xff_u8, 0xb8_u8, 0xa8_u8, - 0xe0_u8, 0xb9_u8, 0x84_u8, - 0xe0_u8, 0xb8_u8, 0x97_u8, - 0xe0_u8, 0xb8_u8, 0xa2_u8, - 0xe4_u8, 0xb8_u8, 0xad_u8, - 0xe5_u8, 0x8d_u8, 0x8e_u8, - 0x56_u8, 0x69_u8, 0xe1_u8, - 0xbb_u8, 0x87_u8, 0x74_u8, - 0x20_u8, 0x4e_u8, 0x61_u8, - 0x6d_u8, 0x60_u8]; - - let _x = from_bytes_with_null(bb); - } - #[test] fn test_from_buf() { unsafe { From 0512475fdab549182e73a42c2cd02df0cb710ebf Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 7 Jul 2013 00:54:41 -0700 Subject: [PATCH 08/26] extra: make sure time::match_digits does not read past the end of the str --- src/libextra/time.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/libextra/time.rs b/src/libextra/time.rs index d95ac6d6c25b4..9cc5d3ba6cca7 100644 --- a/src/libextra/time.rs +++ b/src/libextra/time.rs @@ -287,10 +287,14 @@ priv fn do_strptime(s: &str, format: &str) -> Result { fn match_digits(ss: &str, pos: uint, digits: uint, ws: bool) -> Option<(i32, uint)> { let mut pos = pos; + let len = ss.len(); let mut value = 0_i32; let mut i = 0u; while i < digits { + if pos >= len { + return None; + } let range = ss.char_range_at(pos); pos = range.next; @@ -856,7 +860,7 @@ priv fn do_strftime(format: &str, tm: &Tm) -> ~str { #[cfg(test)] mod tests { - use time::*; + use super::*; use std::float; use std::os; @@ -904,7 +908,7 @@ mod tests { os::setenv("TZ", "America/Los_Angeles"); tzset(); - let time = ::time::Timespec::new(1234567890, 54321); + let time = Timespec::new(1234567890, 54321); let utc = at_utc(time); assert!(utc.tm_sec == 30_i32); @@ -925,7 +929,7 @@ mod tests { os::setenv("TZ", "America/Los_Angeles"); tzset(); - let time = ::time::Timespec::new(1234567890, 54321); + let time = Timespec::new(1234567890, 54321); let local = at(time); error!("time_at: %?", local); @@ -953,7 +957,7 @@ mod tests { os::setenv("TZ", "America/Los_Angeles"); tzset(); - let time = ::time::Timespec::new(1234567890, 54321); + let time = Timespec::new(1234567890, 54321); let utc = at_utc(time); assert_eq!(utc.to_timespec(), time); @@ -964,7 +968,7 @@ mod tests { os::setenv("TZ", "America/Los_Angeles"); tzset(); - let time = ::time::Timespec::new(1234567890, 54321); + let time = Timespec::new(1234567890, 54321); let utc = at_utc(time); let local = at(time); @@ -1145,7 +1149,7 @@ mod tests { os::setenv("TZ", "America/Los_Angeles"); tzset(); - let time = ::time::Timespec::new(1234567890, 54321); + let time = Timespec::new(1234567890, 54321); let utc = at_utc(time); let local = at(time); @@ -1159,7 +1163,7 @@ mod tests { os::setenv("TZ", "America/Los_Angeles"); tzset(); - let time = ::time::Timespec::new(1234567890, 54321); + let time = Timespec::new(1234567890, 54321); let utc = at_utc(time); let local = at(time); From 3102b1797e24b9dd8eef2f68a74ec83749d7b53d Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sat, 3 Aug 2013 17:13:14 -0700 Subject: [PATCH 09/26] std: replace str::as_c_str with std::c_str --- src/libextra/rl.rs | 12 +- src/librustc/back/link.rs | 23 +- src/librustc/back/passes.rs | 3 +- src/librustc/lib/llvm.rs | 4 +- src/librustc/metadata/loader.rs | 3 +- src/librustc/middle/trans/asm.rs | 5 +- src/librustc/middle/trans/base.rs | 43 ++-- src/librustc/middle/trans/builder.rs | 6 +- src/librustc/middle/trans/common.rs | 7 +- src/librustc/middle/trans/consts.rs | 5 +- src/librustc/middle/trans/context.rs | 13 +- src/librustc/middle/trans/controlflow.rs | 3 +- src/librustc/middle/trans/debuginfo.rs | 41 ++-- src/librustc/middle/trans/expr.rs | 2 +- src/librustc/middle/trans/glue.rs | 3 +- src/librustc/middle/trans/meth.rs | 3 +- src/librustc/middle/trans/type_.rs | 3 +- src/librustpkg/util.rs | 5 +- src/libstd/c_str.rs | 291 +++++++++++++++++++++++ src/libstd/io.rs | 14 +- src/libstd/os.rs | 63 ++--- src/libstd/path.rs | 21 +- src/libstd/prelude.rs | 1 + src/libstd/ptr.rs | 23 +- src/libstd/rt/borrowck.rs | 7 +- src/libstd/rt/logging.rs | 4 +- src/libstd/rt/uv/uvio.rs | 26 +- src/libstd/rt/uv/uvll.rs | 5 +- src/libstd/run.rs | 15 +- src/libstd/std.rs | 1 + src/libstd/str.rs | 98 -------- src/libstd/sys.rs | 10 +- src/libstd/unstable/dynamic_lib.rs | 6 +- src/libstd/unstable/lang.rs | 3 +- src/test/run-pass/foreign-fn-linkname.rs | 5 +- 35 files changed, 509 insertions(+), 268 deletions(-) create mode 100644 src/libstd/c_str.rs diff --git a/src/libextra/rl.rs b/src/libextra/rl.rs index 8aff8d388877f..9106d4cd684d9 100644 --- a/src/libextra/rl.rs +++ b/src/libextra/rl.rs @@ -11,7 +11,7 @@ // FIXME #3921. This is unsafe because linenoise uses global mutable // state without mutexes. - +use std::c_str::ToCStr; use std::libc::{c_char, c_int}; use std::local_data; use std::str; @@ -32,7 +32,7 @@ pub mod rustrt { /// Add a line to history pub unsafe fn add_history(line: &str) -> bool { - do line.as_c_str |buf| { + do line.to_c_str().with_ref |buf| { rustrt::linenoiseHistoryAdd(buf) == 1 as c_int } } @@ -44,21 +44,21 @@ pub unsafe fn set_history_max_len(len: int) -> bool { /// Save line history to a file pub unsafe fn save_history(file: &str) -> bool { - do file.as_c_str |buf| { + do file.to_c_str().with_ref |buf| { rustrt::linenoiseHistorySave(buf) == 1 as c_int } } /// Load line history from a file pub unsafe fn load_history(file: &str) -> bool { - do file.as_c_str |buf| { + do file.to_c_str().with_ref |buf| { rustrt::linenoiseHistoryLoad(buf) == 1 as c_int } } /// Print out a prompt and then wait for input and return it pub unsafe fn read(prompt: &str) -> Option<~str> { - do prompt.as_c_str |buf| { + do prompt.to_c_str().with_ref |buf| { let line = rustrt::linenoise(buf); if line.is_null() { None } @@ -80,7 +80,7 @@ pub unsafe fn complete(cb: CompletionCb) { unsafe { do cb(str::raw::from_c_str(line)) |suggestion| { - do suggestion.as_c_str |buf| { + do suggestion.to_c_str().with_ref |buf| { rustrt::linenoiseAddCompletion(completions, buf); } } diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 02cb7f168ce7b..3d02d50b49125 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -22,6 +22,7 @@ use middle::trans::common::gensym_name; use middle::ty; use util::ppaux; +use std::c_str::ToCStr; use std::char; use std::hash::Streaming; use std::hash; @@ -76,9 +77,9 @@ pub fn WriteOutputFile(sess: Session, OptLevel: c_int, EnableSegmentedStacks: bool) { unsafe { - do Triple.as_c_str |Triple| { - do Feature.as_c_str |Feature| { - do Output.as_c_str |Output| { + do Triple.to_c_str().with_ref |Triple| { + do Feature.to_c_str().with_ref |Feature| { + do Output.to_c_str().with_ref |Output| { let result = llvm::LLVMRustWriteOutputFile( PM, M, @@ -105,6 +106,7 @@ pub mod jit { use lib::llvm::{ModuleRef, ContextRef, ExecutionEngineRef}; use metadata::cstore; + use std::c_str::ToCStr; use std::cast; use std::local_data; use std::unstable::intrinsics; @@ -146,7 +148,7 @@ pub mod jit { debug!("linking: %s", path); - do path.as_c_str |buf_t| { + do path.to_c_str().with_ref |buf_t| { if !llvm::LLVMRustLoadCrate(manager, buf_t) { llvm_err(sess, ~"Could not link"); } @@ -165,7 +167,7 @@ pub mod jit { // Next, we need to get a handle on the _rust_main function by // looking up it's corresponding ValueRef and then requesting that // the execution engine compiles the function. - let fun = do "_rust_main".as_c_str |entry| { + let fun = do "_rust_main".to_c_str().with_ref |entry| { llvm::LLVMGetNamedFunction(m, entry) }; if fun.is_null() { @@ -230,6 +232,7 @@ pub mod write { use back::passes; + use std::c_str::ToCStr; use std::libc::{c_int, c_uint}; use std::path::Path; use std::run; @@ -263,14 +266,14 @@ pub mod write { output_type_bitcode => { if opts.optimize != session::No { let filename = output.with_filetype("no-opt.bc"); - do filename.to_str().as_c_str |buf| { + do filename.to_c_str().with_ref |buf| { llvm::LLVMWriteBitcodeToFile(llmod, buf); } } } _ => { let filename = output.with_filetype("bc"); - do filename.to_str().as_c_str |buf| { + do filename.to_c_str().with_ref |buf| { llvm::LLVMWriteBitcodeToFile(llmod, buf); } } @@ -333,7 +336,7 @@ pub mod write { // Always output the bitcode file with --save-temps let filename = output.with_filetype("opt.bc"); - do filename.to_str().as_c_str |buf| { + do filename.to_c_str().with_ref |buf| { llvm::LLVMWriteBitcodeToFile(llmod, buf) }; // Save the assembly file if -S is used @@ -391,13 +394,13 @@ pub mod write { if output_type == output_type_llvm_assembly { // Given options "-S --emit-llvm": output LLVM assembly - do output.to_str().as_c_str |buf_o| { + do output.to_c_str().with_ref |buf_o| { llvm::LLVMRustAddPrintModulePass(pm.llpm, llmod, buf_o); } } else { // If only a bitcode file is asked for by using the // '--emit-llvm' flag, then output it here - do output.to_str().as_c_str |buf| { + do output.to_c_str().with_ref |buf| { llvm::LLVMWriteBitcodeToFile(llmod, buf); } } diff --git a/src/librustc/back/passes.rs b/src/librustc/back/passes.rs index 831719135b13d..eb70a811fe00b 100644 --- a/src/librustc/back/passes.rs +++ b/src/librustc/back/passes.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::c_str::ToCStr; use std::io; use driver::session::{OptLevel, No, Less, Aggressive}; @@ -173,7 +174,7 @@ pub fn populate_pass_manager(sess: Session, pm: &mut PassManager, pass_list:&[~s } pub fn create_pass(name:&str) -> Option { - do name.as_c_str |s| { + do name.to_c_str().with_ref |s| { unsafe { let p = llvm::LLVMCreatePass(s); if p.is_null() { diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs index 6c631a104aa9f..98f32af2f30aa 100644 --- a/src/librustc/lib/llvm.rs +++ b/src/librustc/lib/llvm.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - +use std::c_str::ToCStr; use std::hashmap::HashMap; use std::libc::{c_uint, c_ushort}; use std::option; @@ -2234,7 +2234,7 @@ pub struct TargetData { } pub fn mk_target_data(string_rep: &str) -> TargetData { - let lltd = do string_rep.as_c_str |buf| { + let lltd = do string_rep.to_c_str().with_ref |buf| { unsafe { llvm::LLVMCreateTargetData(buf) } }; diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 89a53df731695..8b41277a3dab7 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -24,6 +24,7 @@ use syntax::print::pprust; use syntax::{ast, attr}; use syntax::attr::AttrMetaMethods; +use std::c_str::ToCStr; use std::cast; use std::io; use std::num; @@ -186,7 +187,7 @@ pub fn metadata_matches(extern_metas: &[@ast::MetaItem], fn get_metadata_section(os: os, filename: &Path) -> Option<@~[u8]> { unsafe { - let mb = do filename.to_str().as_c_str |buf| { + let mb = do filename.to_c_str().with_ref |buf| { llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf) }; if mb as int == 0 { return option::None::<@~[u8]>; } diff --git a/src/librustc/middle/trans/asm.rs b/src/librustc/middle/trans/asm.rs index 2c7e3ae0043c0..9973599e33954 100644 --- a/src/librustc/middle/trans/asm.rs +++ b/src/librustc/middle/trans/asm.rs @@ -12,6 +12,7 @@ # Translation of inline assembly. */ +use std::c_str::ToCStr; use lib; use middle::trans::build::*; @@ -122,8 +123,8 @@ pub fn trans_inline_asm(bcx: @mut Block, ia: &ast::inline_asm) -> @mut Block { ast::asm_intel => lib::llvm::AD_Intel }; - let r = do ia.asm.as_c_str |a| { - do constraints.as_c_str |c| { + let r = do ia.asm.to_c_str().with_ref |a| { + do constraints.to_c_str().with_ref |c| { InlineAsmCall(bcx, a, c, inputs, output, ia.volatile, ia.alignstack, dialect) } }; diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index e0a7cd8cc0b57..5b048558fd117 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -65,6 +65,7 @@ use util::ppaux::{Repr, ty_to_str}; use middle::trans::type_::Type; +use std::c_str::ToCStr; use std::hash; use std::hashmap::HashMap; use std::io; @@ -179,7 +180,7 @@ impl<'self> Drop for StatRecorder<'self> { } pub fn decl_fn(llmod: ModuleRef, name: &str, cc: lib::llvm::CallConv, ty: Type) -> ValueRef { - let llfn: ValueRef = do name.as_c_str |buf| { + let llfn: ValueRef = do name.to_c_str().with_ref |buf| { unsafe { llvm::LLVMGetOrInsertFunction(llmod, buf, ty.to_ref()) } @@ -219,7 +220,7 @@ pub fn get_extern_const(externs: &mut ExternMap, llmod: ModuleRef, None => () } unsafe { - let c = do name.as_c_str |buf| { + let c = do name.to_c_str().with_ref |buf| { llvm::LLVMAddGlobal(llmod, ty.to_ref(), buf) }; externs.insert(name, c); @@ -548,7 +549,7 @@ pub fn get_res_dtor(ccx: @mut CrateContext, // Structural comparison: a rather involved form of glue. pub fn maybe_name_value(cx: &CrateContext, v: ValueRef, s: &str) { if cx.sess.opts.save_temps { - do s.as_c_str |buf| { + do s.to_c_str().with_ref |buf| { unsafe { llvm::LLVMSetValueName(v, buf) } @@ -1162,7 +1163,7 @@ pub fn new_block(cx: @mut FunctionContext, opt_node_info: Option) -> @mut Block { unsafe { - let llbb = do name.as_c_str |buf| { + let llbb = do name.to_c_str().with_ref |buf| { llvm::LLVMAppendBasicBlockInContext(cx.ccx.llcx, cx.llfn, buf) }; let bcx = @mut Block::new(llbb, @@ -1579,7 +1580,7 @@ pub struct BasicBlocks { pub fn mk_staticallocas_basic_block(llfn: ValueRef) -> BasicBlockRef { unsafe { let cx = task_llcx(); - do "static_allocas".as_c_str | buf| { + do "static_allocas".to_c_str().with_ref | buf| { llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf) } } @@ -1588,7 +1589,7 @@ pub fn mk_staticallocas_basic_block(llfn: ValueRef) -> BasicBlockRef { pub fn mk_return_basic_block(llfn: ValueRef) -> BasicBlockRef { unsafe { let cx = task_llcx(); - do "return".as_c_str |buf| { + do "return".to_c_str().with_ref |buf| { llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf) } } @@ -2346,7 +2347,7 @@ pub fn create_entry_wrapper(ccx: @mut CrateContext, }; decl_cdecl_fn(ccx.llmod, main_name, llfty) }; - let llbb = do "top".as_c_str |buf| { + let llbb = do "top".to_c_str().with_ref |buf| { unsafe { llvm::LLVMAppendBasicBlockInContext(ccx.llcx, llfn, buf) } @@ -2356,7 +2357,7 @@ pub fn create_entry_wrapper(ccx: @mut CrateContext, llvm::LLVMPositionBuilderAtEnd(bld, llbb); let crate_map = ccx.crate_map; - let opaque_crate_map = do "crate_map".as_c_str |buf| { + let opaque_crate_map = do "crate_map".to_c_str().with_ref |buf| { llvm::LLVMBuildPointerCast(bld, crate_map, Type::i8p().to_ref(), buf) }; @@ -2374,7 +2375,7 @@ pub fn create_entry_wrapper(ccx: @mut CrateContext, }; let args = { - let opaque_rust_main = do "rust_main".as_c_str |buf| { + let opaque_rust_main = do "rust_main".to_c_str().with_ref |buf| { llvm::LLVMBuildPointerCast(bld, rust_main, Type::i8p().to_ref(), buf) }; @@ -2462,7 +2463,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef { unsafe { let llty = llvm::LLVMTypeOf(v); - let g = do sym.as_c_str |buf| { + let g = do sym.to_c_str().with_ref |buf| { llvm::LLVMAddGlobal(ccx.llmod, llty, buf) }; @@ -2486,7 +2487,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef { match (attr::first_attr_value_str_by_name(i.attrs, "link_section")) { Some(sect) => unsafe { - do sect.as_c_str |buf| { + do sect.to_c_str().with_ref |buf| { llvm::LLVMSetSection(v, buf); } }, @@ -2527,7 +2528,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef { } ast::foreign_item_static(*) => { let ident = token::ident_to_str(&ni.ident); - let g = do ident.as_c_str |buf| { + let g = do ident.to_c_str().with_ref |buf| { unsafe { let ty = type_of(ccx, ty); llvm::LLVMAddGlobal(ccx.llmod, ty.to_ref(), buf) @@ -2633,7 +2634,7 @@ pub fn trans_constant(ccx: &mut CrateContext, it: @ast::item) { let s = mangle_exported_name(ccx, p, ty::mk_int()).to_managed(); let disr_val = vi[i].disr_val; note_unique_llvm_symbol(ccx, s); - let discrim_gvar = do s.as_c_str |buf| { + let discrim_gvar = do s.to_c_str().with_ref |buf| { unsafe { llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type.to_ref(), buf) } @@ -2774,7 +2775,7 @@ pub fn decl_gc_metadata(ccx: &mut CrateContext, llmod_id: &str) { } let gc_metadata_name = ~"_gc_module_metadata_" + llmod_id; - let gc_metadata = do gc_metadata_name.as_c_str |buf| { + let gc_metadata = do gc_metadata_name.to_c_str().with_ref |buf| { unsafe { llvm::LLVMAddGlobal(ccx.llmod, Type::i32().to_ref(), buf) } @@ -2789,7 +2790,7 @@ pub fn decl_gc_metadata(ccx: &mut CrateContext, llmod_id: &str) { pub fn create_module_map(ccx: &mut CrateContext) -> ValueRef { let elttype = Type::struct_([ccx.int_type, ccx.int_type], false); let maptype = Type::array(&elttype, (ccx.module_data.len() + 1) as u64); - let map = do "_rust_mod_map".as_c_str |buf| { + let map = do "_rust_mod_map".to_c_str().with_ref |buf| { unsafe { llvm::LLVMAddGlobal(ccx.llmod, maptype.to_ref(), buf) } @@ -2837,7 +2838,7 @@ pub fn decl_crate_map(sess: session::Session, mapmeta: LinkMeta, let sym_name = ~"_rust_crate_map_" + mapname; let arrtype = Type::array(&int_type, n_subcrates as u64); let maptype = Type::struct_([Type::i32(), Type::i8p(), int_type, arrtype], false); - let map = do sym_name.as_c_str |buf| { + let map = do sym_name.to_c_str().with_ref |buf| { unsafe { llvm::LLVMAddGlobal(llmod, maptype.to_ref(), buf) } @@ -2856,7 +2857,7 @@ pub fn fill_crate_map(ccx: @mut CrateContext, map: ValueRef) { cdata.name, cstore::get_crate_vers(cstore, i), cstore::get_crate_hash(cstore, i)); - let cr = do nm.as_c_str |buf| { + let cr = do nm.to_c_str().with_ref |buf| { unsafe { llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type.to_ref(), buf) } @@ -2919,21 +2920,21 @@ pub fn write_metadata(cx: &mut CrateContext, crate: &ast::Crate) { let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item); let llmeta = C_bytes(encoder::encode_metadata(encode_parms, crate)); let llconst = C_struct([llmeta]); - let mut llglobal = do "rust_metadata".as_c_str |buf| { + let mut llglobal = do "rust_metadata".to_c_str().with_ref |buf| { unsafe { llvm::LLVMAddGlobal(cx.llmod, val_ty(llconst).to_ref(), buf) } }; unsafe { llvm::LLVMSetInitializer(llglobal, llconst); - do cx.sess.targ_cfg.target_strs.meta_sect_name.as_c_str |buf| { + do cx.sess.targ_cfg.target_strs.meta_sect_name.to_c_str().with_ref |buf| { llvm::LLVMSetSection(llglobal, buf) }; lib::llvm::SetLinkage(llglobal, lib::llvm::InternalLinkage); let t_ptr_i8 = Type::i8p(); llglobal = llvm::LLVMConstBitCast(llglobal, t_ptr_i8.to_ref()); - let llvm_used = do "llvm.used".as_c_str |buf| { + let llvm_used = do "llvm.used".to_c_str().with_ref |buf| { llvm::LLVMAddGlobal(cx.llmod, Type::array(&t_ptr_i8, 1).to_ref(), buf) }; lib::llvm::SetLinkage(llvm_used, lib::llvm::AppendingLinkage); @@ -2947,7 +2948,7 @@ fn mk_global(ccx: &CrateContext, internal: bool) -> ValueRef { unsafe { - let llglobal = do name.as_c_str |buf| { + let llglobal = do name.to_c_str().with_ref |buf| { llvm::LLVMAddGlobal(ccx.llmod, val_ty(llval).to_ref(), buf) }; llvm::LLVMSetInitializer(llglobal, llval); diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs index 9d44dacfd6854..bfdee1b8f34df 100644 --- a/src/librustc/middle/trans/builder.rs +++ b/src/librustc/middle/trans/builder.rs @@ -423,7 +423,7 @@ impl Builder { if name.is_empty() { llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), noname()) } else { - do name.as_c_str |c| { + do name.to_c_str().with_ref |c| { llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), c) } } @@ -739,7 +739,7 @@ impl Builder { let sanitized = text.replace("$", ""); let comment_text = fmt!("# %s", sanitized.replace("\n", "\n\t# ")); self.count_insn("inlineasm"); - let asm = do comment_text.as_c_str |c| { + let asm = do comment_text.to_c_str().with_ref |c| { unsafe { llvm::LLVMConstInlineAsm(Type::func([], &Type::void()).to_ref(), c, noname(), False, False) @@ -895,7 +895,7 @@ impl Builder { let BB: BasicBlockRef = llvm::LLVMGetInsertBlock(self.llbuilder); let FN: ValueRef = llvm::LLVMGetBasicBlockParent(BB); let M: ModuleRef = llvm::LLVMGetGlobalParent(FN); - let T: ValueRef = do "llvm.trap".as_c_str |buf| { + let T: ValueRef = do "llvm.trap".to_c_str().with_ref |buf| { llvm::LLVMGetNamedFunction(M, buf) }; assert!((T as int != 0)); diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index df59133d9706e..4f5fae8db1a4c 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -31,6 +31,7 @@ use util::ppaux::{Repr}; use middle::trans::type_::Type; +use std::c_str::ToCStr; use std::cast::transmute; use std::cast; use std::hashmap::{HashMap}; @@ -707,7 +708,7 @@ pub fn C_integral(t: Type, u: u64, sign_extend: bool) -> ValueRef { pub fn C_floating(s: &str, t: Type) -> ValueRef { unsafe { - do s.as_c_str |buf| { + do s.to_c_str().with_ref |buf| { llvm::LLVMConstRealOfString(t.to_ref(), buf) } } @@ -755,12 +756,12 @@ pub fn C_cstr(cx: &mut CrateContext, s: @str) -> ValueRef { None => () } - let sc = do s.as_c_str |buf| { + let sc = do s.to_c_str().with_ref |buf| { llvm::LLVMConstStringInContext(cx.llcx, buf, s.len() as c_uint, False) }; let gsym = token::gensym("str"); - let g = do fmt!("str%u", gsym).as_c_str |buf| { + let g = do fmt!("str%u", gsym).to_c_str().with_ref |buf| { llvm::LLVMAddGlobal(cx.llmod, val_ty(sc).to_ref(), buf) }; llvm::LLVMSetInitializer(g, sc); diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 40d0d77c16ecb..0cb423d4921eb 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -30,6 +30,7 @@ use util::ppaux::{Repr, ty_to_str}; use middle::trans::type_::Type; +use std::c_str::ToCStr; use std::libc::c_uint; use syntax::{ast, ast_util, ast_map}; @@ -101,7 +102,7 @@ pub fn const_vec(cx: @mut CrateContext, e: &ast::expr, es: &[@ast::expr]) fn const_addr_of(cx: &mut CrateContext, cv: ValueRef) -> ValueRef { unsafe { - let gv = do "const".as_c_str |name| { + let gv = do "const".to_c_str().with_ref |name| { llvm::LLVMAddGlobal(cx.llmod, val_ty(cv).to_ref(), name) }; llvm::LLVMSetInitializer(gv, cv); @@ -527,7 +528,7 @@ fn const_expr_unadjusted(cx: @mut CrateContext, e: &ast::expr) -> ValueRef { ast::expr_vec(ref es, ast::m_imm) => { let (cv, sz, llunitty) = const_vec(cx, e, *es); let llty = val_ty(cv); - let gv = do "const".as_c_str |name| { + let gv = do "const".to_c_str().with_ref |name| { llvm::LLVMAddGlobal(cx.llmod, llty.to_ref(), name) }; llvm::LLVMSetInitializer(gv, cv); diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 802163583d6a1..54c957d115a4d 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -26,6 +26,7 @@ use middle::ty; use middle::trans::type_::Type; +use std::c_str::ToCStr; use std::hash; use std::hashmap::{HashMap, HashSet}; use std::local_data; @@ -124,11 +125,17 @@ impl CrateContext { unsafe { let llcx = llvm::LLVMContextCreate(); set_task_llcx(llcx); - let llmod = name.as_c_str(|buf| llvm::LLVMModuleCreateWithNameInContext(buf, llcx)); + let llmod = do name.to_c_str().with_ref |buf| { + llvm::LLVMModuleCreateWithNameInContext(buf, llcx) + }; let data_layout: &str = sess.targ_cfg.target_strs.data_layout; let targ_triple: &str = sess.targ_cfg.target_strs.target_triple; - data_layout.as_c_str(|buf| llvm::LLVMSetDataLayout(llmod, buf)); - targ_triple.as_c_str(|buf| llvm::LLVMSetTarget(llmod, buf)); + do data_layout.to_c_str().with_ref |buf| { + llvm::LLVMSetDataLayout(llmod, buf) + }; + do targ_triple.to_c_str().with_ref |buf| { + llvm::LLVMSetTarget(llmod, buf) + }; let targ_cfg = sess.targ_cfg; let td = mk_target_data(sess.targ_cfg.target_strs.data_layout); diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 7d9eb95505bb7..ec96371132e96 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::c_str::ToCStr; use back::link; use lib; @@ -240,7 +241,7 @@ pub fn trans_log(log_ex: &ast::expr, ccx, modpath, "loglevel"); let global; unsafe { - global = do s.as_c_str |buf| { + global = do s.to_c_str().with_ref |buf| { llvm::LLVMAddGlobal(ccx.llmod, Type::i32().to_ref(), buf) }; llvm::LLVMSetGlobalConstant(global, False); diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index e31a27a4c6ca0..c427c34e1d85d 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -63,6 +63,7 @@ use middle::ty; use middle::pat_util; use util::ppaux::ty_to_str; +use std::c_str::ToCStr; use std::hashmap::HashMap; use std::libc::{c_uint, c_ulonglong, c_longlong}; use std::ptr; @@ -159,7 +160,7 @@ pub fn create_local_var_metadata(bcx: @mut Block, local: &ast::Local) { let ty = node_id_type(bcx, node_id); let type_metadata = type_metadata(cx, ty, span); - let var_metadata = do name.as_c_str |name| { + let var_metadata = do name.to_c_str().with_ref |name| { unsafe { llvm::LLVMDIBuilderCreateLocalVariable( DIB(cx), @@ -246,7 +247,7 @@ pub fn create_argument_metadata(bcx: @mut Block, argument_index as c_uint }; - let arg_metadata = do name.as_c_str |name| { + let arg_metadata = do name.to_c_str().with_ref |name| { unsafe { llvm::LLVMDIBuilderCreateLocalVariable( DIB(cx), @@ -382,8 +383,8 @@ pub fn create_function_metadata(fcx: &FunctionContext) -> DISubprogram { }; let fn_metadata = - do cx.sess.str_of(ident).as_c_str |name| { - do cx.sess.str_of(ident).as_c_str |linkage| { + do cx.sess.str_of(ident).to_c_str().with_ref |name| { + do cx.sess.str_of(ident).to_c_str().with_ref |linkage| { unsafe { llvm::LLVMDIBuilderCreateFunction( DIB(cx), @@ -430,11 +431,11 @@ fn compile_unit_metadata(cx: @mut CrateContext) { let work_dir = cx.sess.working_dir.to_str(); let producer = fmt!("rustc version %s", env!("CFG_VERSION")); - do crate_name.as_c_str |crate_name| { - do work_dir.as_c_str |work_dir| { - do producer.as_c_str |producer| { - do "".as_c_str |flags| { - do "".as_c_str |split_name| { + do crate_name.to_c_str().with_ref |crate_name| { + do work_dir.to_c_str().with_ref |work_dir| { + do producer.to_c_str().with_ref |producer| { + do "".to_c_str().with_ref |flags| { + do "".to_c_str().with_ref |split_name| { unsafe { llvm::LLVMDIBuilderCreateCompileUnit(dcx.builder, DW_LANG_RUST as c_uint, crate_name, work_dir, producer, @@ -461,8 +462,8 @@ fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile { }; let file_metadata = - do file_name.as_c_str |file_name| { - do work_dir.as_c_str |work_dir| { + do file_name.to_c_str().with_ref |file_name| { + do work_dir.to_c_str().with_ref |work_dir| { unsafe { llvm::LLVMDIBuilderCreateFile(DIB(cx), file_name, work_dir) } @@ -550,7 +551,7 @@ fn basic_type_metadata(cx: &mut CrateContext, t: ty::t) -> DIType { let llvm_type = type_of::type_of(cx, t); let (size, align) = size_and_align_of(cx, llvm_type); - let ty_metadata = do name.as_c_str |name| { + let ty_metadata = do name.to_c_str().with_ref |name| { unsafe { llvm::LLVMDIBuilderCreateBasicType( DIB(cx), @@ -571,7 +572,7 @@ fn pointer_type_metadata(cx: &mut CrateContext, let pointer_llvm_type = type_of::type_of(cx, pointer_type); let (pointer_size, pointer_align) = size_and_align_of(cx, pointer_llvm_type); let name = ty_to_str(cx.tcx, pointer_type); - let ptr_metadata = do name.as_c_str |name| { + let ptr_metadata = do name.to_c_str().with_ref |name| { unsafe { llvm::LLVMDIBuilderCreatePointerType( DIB(cx), @@ -665,7 +666,7 @@ fn enum_metadata(cx: &mut CrateContext, let name: &str = cx.sess.str_of(v.name); let discriminant_value = v.disr_val as c_ulonglong; - do name.as_c_str |name| { + do name.to_c_str().with_ref |name| { unsafe { llvm::LLVMDIBuilderCreateEnumerator( DIB(cx), @@ -679,7 +680,7 @@ fn enum_metadata(cx: &mut CrateContext, let loc = span_start(cx, span); let file_metadata = file_metadata(cx, loc.file.name); - let discriminant_type_metadata = do enum_name.as_c_str |enum_name| { + let discriminant_type_metadata = do enum_name.to_c_str().with_ref |enum_name| { unsafe { llvm::LLVMDIBuilderCreateEnumerationType( DIB(cx), @@ -716,7 +717,7 @@ fn enum_metadata(cx: &mut CrateContext, Some(discriminant_type_metadata), span); - do "".as_c_str |name| { + do "".to_c_str().with_ref |name| { unsafe { llvm::LLVMDIBuilderCreateMemberType( DIB(cx), @@ -736,7 +737,7 @@ fn enum_metadata(cx: &mut CrateContext, let enum_llvm_type = type_of::type_of(cx, enum_type); let (enum_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type); - return do enum_name.as_c_str |enum_name| { + return do enum_name.to_c_str().with_ref |enum_name| { unsafe { llvm::LLVMDIBuilderCreateUnionType( DIB(cx), @@ -820,7 +821,7 @@ fn composite_type_metadata(cx: &mut CrateContext, let member_offset = machine::llelement_offset(cx, composite_llvm_type, i); let member_name: &str = member_names[i]; - do member_name.as_c_str |member_name| { + do member_name.to_c_str().with_ref |member_name| { unsafe { llvm::LLVMDIBuilderCreateMemberType( DIB(cx), @@ -838,7 +839,7 @@ fn composite_type_metadata(cx: &mut CrateContext, }) .collect(); - return do composite_type_name.as_c_str |name| { + return do composite_type_name.to_c_str().with_ref |name| { unsafe { llvm::LLVMDIBuilderCreateStructType( DIB(cx), @@ -1064,7 +1065,7 @@ fn unimplemented_type_metadata(cx: &mut CrateContext, t: ty::t) -> DIType { debug!("unimplemented_type_metadata: %?", ty::get(t)); let name = ty_to_str(cx.tcx, t); - let metadata = do fmt!("NYI<%s>", name).as_c_str |name| { + let metadata = do fmt!("NYI<%s>", name).to_c_str().with_ref |name| { unsafe { llvm::LLVMDIBuilderCreateBasicType( DIB(cx), diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index c5b3d328f3042..10586dbe55b1a 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -964,7 +964,7 @@ fn trans_lvalue_unadjusted(bcx: @mut Block, expr: @ast::expr) -> DatumBlock { let symbol = csearch::get_symbol( bcx.ccx().sess.cstore, did); - let llval = do symbol.as_c_str |buf| { + let llval = do symbol.to_c_str().with_ref |buf| { llvm::LLVMAddGlobal(bcx.ccx().llmod, llty.to_ref(), buf) diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index bbf07e369750f..c0a5795e87de1 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -37,6 +37,7 @@ use util::ppaux::ty_to_short_str; use middle::trans::type_::Type; +use std::c_str::ToCStr; use std::libc::c_uint; use syntax::ast; @@ -659,7 +660,7 @@ pub fn declare_tydesc(ccx: &mut CrateContext, t: ty::t) -> @mut tydesc_info { let name = mangle_internal_name_by_type_and_seq(ccx, t, "tydesc").to_managed(); note_unique_llvm_symbol(ccx, name); debug!("+++ declare_tydesc %s %s", ppaux::ty_to_str(ccx.tcx, t), name); - let gvar = do name.as_c_str |buf| { + let gvar = do name.to_c_str().with_ref |buf| { unsafe { llvm::LLVMAddGlobal(ccx.llmod, ccx.tydesc_type.to_ref(), buf) } diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index debaf0f04a7df..b3939a3105065 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -32,6 +32,7 @@ use util::ppaux::Repr; use middle::trans::type_::Type; +use std::c_str::ToCStr; use std::vec; use syntax::ast_map::{path, path_mod, path_name}; use syntax::ast_util; @@ -605,7 +606,7 @@ pub fn make_vtable(ccx: &mut CrateContext, let tbl = C_struct(components); let vtable = ccx.sess.str_of(gensym_name("vtable")); - let vt_gvar = do vtable.as_c_str |buf| { + let vt_gvar = do vtable.to_c_str().with_ref |buf| { llvm::LLVMAddGlobal(ccx.llmod, val_ty(tbl).to_ref(), buf) }; llvm::LLVMSetInitializer(vt_gvar, tbl); diff --git a/src/librustc/middle/trans/type_.rs b/src/librustc/middle/trans/type_.rs index 9bb7f9571f39f..110febfcc9f57 100644 --- a/src/librustc/middle/trans/type_.rs +++ b/src/librustc/middle/trans/type_.rs @@ -20,6 +20,7 @@ use middle::trans::base; use syntax::ast; use syntax::abi::{Architecture, X86, X86_64, Arm, Mips}; +use std::c_str::ToCStr; use std::vec; use std::cast; @@ -170,7 +171,7 @@ impl Type { pub fn named_struct(name: &str) -> Type { let ctx = base::task_llcx(); - ty!(name.as_c_str(|s| llvm::LLVMStructCreateNamed(ctx, s))) + ty!(name.to_c_str().with_ref(|s| llvm::LLVMStructCreateNamed(ctx, s))) } pub fn empty_struct() -> Type { diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 2010e1d4b793f..7ef2b90297d73 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -9,6 +9,7 @@ // except according to those terms. use std::{os, result}; +use std::c_str::ToCStr; use rustc::driver::{driver, session}; use rustc::metadata::filesearch; use extra::getopts::groups::getopts; @@ -374,8 +375,8 @@ pub fn link_exe(_src: &Path, _dest: &Path) -> bool { pub fn link_exe(src: &Path, dest: &Path) -> bool { use std::libc; unsafe { - do src.to_str().as_c_str |src_buf| { - do dest.to_str().as_c_str |dest_buf| { + do src.to_c_str().with_ref |src_buf| { + do dest.to_c_str().with_ref |dest_buf| { libc::link(src_buf, dest_buf) == 0 as libc::c_int && libc::chmod(dest_buf, 755) == 0 as libc::c_int } diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs new file mode 100644 index 0000000000000..29aa68b1533e4 --- /dev/null +++ b/src/libstd/c_str.rs @@ -0,0 +1,291 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cast; +use iterator::Iterator; +use libc; +use ops::Drop; +use option::{Option, Some, None}; +use ptr::RawPtr; +use ptr; +use str::StrSlice; +use vec::ImmutableVector; + +/** + * 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. + */ +pub struct CString { + priv buf: *libc::c_char, +} + +impl<'self> CString { + /** + * Create a C String from a str. + */ + pub fn from_str(s: &str) -> CString { + s.to_c_str() + } + + /** + * Take the wrapped `*libc::c_char` from the `CString` wrapper. + * + * # Failure + * + * If the wrapper is empty. + */ + pub unsafe fn take(&mut self) -> *libc::c_char { + if self.buf.is_null() { + fail!("CString has no wrapped `*libc::c_char`"); + } + let buf = self.buf; + self.buf = ptr::null(); + buf + } + + /** + * Puts a `*libc::c_char` into the `CString` wrapper. + * + * # Failure + * + * If the `*libc::c_char` is null. + * If the wrapper is not empty. + */ + pub fn put_back(&mut self, buf: *libc::c_char) { + if buf.is_null() { + fail!("attempt to put a null pointer into a CString"); + } + if self.buf.is_not_null() { + fail!("CString already wraps a `*libc::c_char`"); + } + self.buf = buf; + } + + /** + * Calls a closure with a reference to the underlying `*libc::c_char`. + */ + pub fn with_ref(&self, f: &fn(*libc::c_char) -> T) -> T { + if self.buf.is_null() { + fail!("CString already wraps a `*libc::c_char`"); + } + f(self.buf) + } + + /** + * Calls a closure with a mutable reference to the underlying `*libc::c_char`. + */ + pub fn with_mut_ref(&mut self, f: &fn(*mut libc::c_char) -> T) -> T { + if self.buf.is_not_null() { + fail!("CString already wraps a `*libc::c_char`"); + } + f(unsafe { cast::transmute(self.buf) }) + } + + /** + * Returns true if the CString does not wrap a `*libc::c_char`. + */ + pub fn is_empty(&self) -> bool { + self.buf.is_null() + } + + /** + * Returns true if the CString wraps a `*libc::c_char`. + */ + pub fn is_not_empty(&self) -> bool { + self.buf.is_not_null() + } + + /** + * Converts the CString into a `&[u8]` without copying. + */ + pub fn as_bytes(&self) -> &'self [u8] { + unsafe { + let len = libc::strlen(self.buf) as uint; + cast::transmute((self.buf, len + 1)) + } + } + + /** + * Return a CString iterator. + */ + fn iter(&self) -> CStringIterator<'self> { + CStringIterator { + ptr: self.buf, + lifetime: unsafe { cast::transmute(self.buf) }, + } + } +} + +impl Drop for CString { + fn drop(&self) { + if self.buf.is_not_null() { + unsafe { + libc::free(self.buf as *libc::c_void) + }; + } + } +} + +/** + * A generic trait for converting a value to a CString. + */ +pub trait ToCStr { + /** + * Create a C String. + */ + fn to_c_str(&self) -> CString; +} + +impl<'self> ToCStr for &'self str { + /** + * Create a C String from a `&str`. + */ + fn to_c_str(&self) -> CString { + self.as_bytes().to_c_str() + } +} + +impl<'self> ToCStr for &'self [u8] { + /** + * Create a C String from a `&[u8]`. + */ + fn to_c_str(&self) -> CString { + do self.as_imm_buf |self_buf, self_len| { + unsafe { + let buf = libc::malloc(self_len as u64 + 1) as *mut u8; + if buf.is_null() { + fail!("failed to allocate memory!"); + } + + ptr::copy_memory(buf, self_buf, self_len); + *ptr::mut_offset(buf, self_len as int) = 0; + CString { buf: buf as *libc::c_char } + } + } + } +} + +/** + * External iterator for a CString's bytes. + * + * Use with the `std::iterator` module. + */ +pub struct CStringIterator<'self> { + priv ptr: *libc::c_char, + priv lifetime: &'self libc::c_char, // FIXME: #5922 +} + +impl<'self> Iterator for CStringIterator<'self> { + /** + * Advance the iterator. + */ + fn next(&mut self) -> Option { + if self.ptr.is_null() { + None + } else { + let ch = unsafe { *self.ptr }; + self.ptr = ptr::offset(self.ptr, 1); + Some(ch) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use libc; + use ptr; + + #[test] + fn test_to_c_str() { + do "".to_c_str().with_ref |buf| { + unsafe { + assert_eq!(*ptr::offset(buf, 0), 0); + } + } + + do "hello".to_c_str().with_ref |buf| { + unsafe { + assert_eq!(*ptr::offset(buf, 0), 'h' as libc::c_char); + assert_eq!(*ptr::offset(buf, 1), 'e' as libc::c_char); + assert_eq!(*ptr::offset(buf, 2), 'l' as libc::c_char); + assert_eq!(*ptr::offset(buf, 3), 'l' as libc::c_char); + assert_eq!(*ptr::offset(buf, 4), 'o' as libc::c_char); + assert_eq!(*ptr::offset(buf, 5), 0); + } + } + } + + #[test] + fn test_take() { + let mut c_str = "hello".to_c_str(); + unsafe { libc::free(c_str.take() as *libc::c_void) } + assert!(c_str.is_empty()); + } + + #[test] + fn test_take_and_put_back() { + let mut c_str = "hello".to_c_str(); + assert!(c_str.is_not_empty()); + + let buf = unsafe { c_str.take() }; + + assert!(c_str.is_empty()); + + c_str.put_back(buf); + + assert!(c_str.is_not_empty()); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_take_empty_fail() { + let mut c_str = "hello".to_c_str(); + unsafe { + libc::free(c_str.take() as *libc::c_void); + c_str.take(); + } + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_put_back_null_fail() { + let mut c_str = "hello".to_c_str(); + c_str.put_back(ptr::null()); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_put_back_full_fail() { + let mut c_str = "hello".to_c_str(); + c_str.put_back(0xdeadbeef as *libc::c_char); + } + + fn test_with() { + let c_str = "hello".to_c_str(); + let len = unsafe { c_str.with_ref(|buf| libc::strlen(buf)) }; + assert!(c_str.is_not_empty()); + assert_eq!(len, 5); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn test_with_empty_fail() { + let mut c_str = "hello".to_c_str(); + unsafe { libc::free(c_str.take() as *libc::c_void) } + c_str.with_ref(|_| ()); + } +} diff --git a/src/libstd/io.rs b/src/libstd/io.rs index 606c958b4085e..78c6e8d5342de 100644 --- a/src/libstd/io.rs +++ b/src/libstd/io.rs @@ -48,6 +48,7 @@ implement `Reader` and `Writer`, where appropriate. use cast; use clone::Clone; +use c_str::ToCStr; use container::Container; use int; use iterator::Iterator; @@ -1040,8 +1041,8 @@ pub fn stdin() -> @Reader { } pub fn file_reader(path: &Path) -> Result<@Reader, ~str> { - let f = do path.to_str().as_c_str |pathbuf| { - do "r".as_c_str |modebuf| { + let f = do path.to_c_str().with_ref |pathbuf| { + do "r".to_c_str().with_ref |modebuf| { unsafe { libc::fopen(pathbuf, modebuf as *libc::c_char) } } }; @@ -1290,9 +1291,8 @@ pub fn mk_file_writer(path: &Path, flags: &[FileFlag]) } } let fd = unsafe { - do path.to_str().as_c_str |pathbuf| { - libc::open(pathbuf, fflags, - (S_IRUSR | S_IWUSR) as c_int) + do path.to_c_str().with_ref |pathbuf| { + libc::open(pathbuf, fflags, (S_IRUSR | S_IWUSR) as c_int) } }; if fd < (0 as c_int) { @@ -1574,8 +1574,8 @@ pub fn file_writer(path: &Path, flags: &[FileFlag]) -> Result<@Writer, ~str> { // FIXME: fileflags // #2004 pub fn buffered_file_writer(path: &Path) -> Result<@Writer, ~str> { unsafe { - let f = do path.to_str().as_c_str |pathbuf| { - do "w".as_c_str |modebuf| { + let f = do path.to_c_str().with_ref |pathbuf| { + do "w".to_c_str().with_ref |modebuf| { libc::fopen(pathbuf, modebuf) } }; diff --git a/src/libstd/os.rs b/src/libstd/os.rs index f17a59db38f5a..fb4f14d33c60b 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -28,6 +28,7 @@ #[allow(missing_doc)]; +use c_str::ToCStr; use cast; use clone::Clone; use container::Container; @@ -241,7 +242,9 @@ pub fn env() -> ~[(~str,~str)] { pub fn getenv(n: &str) -> Option<~str> { unsafe { do with_env_lock { - let s = n.as_c_str(|s| libc::getenv(s as *libc::c_char)); + let s = do n.to_c_str().with_ref |buf| { + libc::getenv(buf) + }; if ptr::null::() == cast::transmute(s) { None } else { @@ -274,8 +277,8 @@ pub fn getenv(n: &str) -> Option<~str> { pub fn setenv(n: &str, v: &str) { unsafe { do with_env_lock { - do n.to_str().as_c_str |nbuf| { - do v.to_str().as_c_str |vbuf| { + do n.to_c_str().with_ref |nbuf| { + do v.to_c_str().with_ref |vbuf| { libc::funcs::posix01::unistd::setenv(nbuf, vbuf, 1); } } @@ -306,7 +309,7 @@ pub fn unsetenv(n: &str) { fn _unsetenv(n: &str) { unsafe { do with_env_lock { - do n.to_str().as_c_str |nbuf| { + do n.to_c_str().with_ref |nbuf| { libc::funcs::posix01::unistd::unsetenv(nbuf); } } @@ -328,7 +331,7 @@ pub fn unsetenv(n: &str) { } pub fn fdopen(fd: c_int) -> *FILE { - do "r".as_c_str |modebuf| { + do "r".to_c_str().with_ref |modebuf| { unsafe { libc::fdopen(fd, modebuf) } @@ -462,9 +465,9 @@ pub fn self_exe_path() -> Option { use libc::funcs::posix01::unistd::readlink; let mut path_str = str::with_capacity(TMPBUF_SZ); - let len = do path_str.as_c_str |buf| { + let len = do path_str.to_c_str().with_ref |buf| { let buf = buf as *mut c_char; - do "/proc/self/exe".as_c_str |proc_self_buf| { + do "/proc/self/exe".to_c_str().with_ref |proc_self_buf| { readlink(proc_self_buf, buf, TMPBUF_SZ as size_t) } }; @@ -595,7 +598,7 @@ pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool { /// Indicates whether a path represents a directory pub fn path_is_dir(p: &Path) -> bool { unsafe { - do p.to_str().as_c_str |buf| { + do p.to_c_str().with_ref |buf| { rustrt::rust_path_is_dir(buf) != 0 as c_int } } @@ -604,7 +607,7 @@ pub fn path_is_dir(p: &Path) -> bool { /// Indicates whether a path exists pub fn path_exists(p: &Path) -> bool { unsafe { - do p.to_str().as_c_str |buf| { + do p.to_c_str().with_ref |buf| { rustrt::rust_path_exists(buf) != 0 as c_int } } @@ -647,7 +650,7 @@ pub fn make_dir(p: &Path, mode: c_int) -> bool { #[cfg(unix)] fn mkdir(p: &Path, mode: c_int) -> bool { - do p.to_str().as_c_str |buf| { + do p.to_c_str().with_ref |buf| { unsafe { libc::mkdir(buf, mode as libc::mode_t) == (0 as c_int) } @@ -823,7 +826,7 @@ pub fn remove_dir(p: &Path) -> bool { #[cfg(unix)] fn rmdir(p: &Path) -> bool { - do p.to_str().as_c_str |buf| { + do p.to_c_str().with_ref |buf| { unsafe { libc::rmdir(buf) == (0 as c_int) } @@ -848,7 +851,7 @@ pub fn change_dir(p: &Path) -> bool { #[cfg(unix)] fn chdir(p: &Path) -> bool { - do p.to_str().as_c_str |buf| { + do p.to_c_str().with_ref |buf| { unsafe { libc::chdir(buf) == (0 as c_int) } @@ -876,8 +879,8 @@ pub fn copy_file(from: &Path, to: &Path) -> bool { #[cfg(unix)] fn do_copy_file(from: &Path, to: &Path) -> bool { unsafe { - let istream = do from.to_str().as_c_str |fromp| { - do "rb".as_c_str |modebuf| { + let istream = do from.to_c_str().with_ref |fromp| { + do "rb".to_c_str().with_ref |modebuf| { libc::fopen(fromp, modebuf) } }; @@ -888,8 +891,8 @@ pub fn copy_file(from: &Path, to: &Path) -> bool { let from_mode = from.get_mode().expect("copy_file: couldn't get permissions \ for source file"); - let ostream = do to.to_str().as_c_str |top| { - do "w+b".as_c_str |modebuf| { + let ostream = do to.to_c_str().with_ref |top| { + do "w+b".to_c_str().with_ref |modebuf| { libc::fopen(top, modebuf) } }; @@ -921,7 +924,7 @@ pub fn copy_file(from: &Path, to: &Path) -> bool { fclose(ostream); // Give the new file the old file's permissions - if do to.to_str().as_c_str |to_buf| { + if do to.to_c_str().with_ref |to_buf| { libc::chmod(to_buf, from_mode as libc::mode_t) } != 0 { return false; // should be a condition... @@ -948,7 +951,7 @@ pub fn remove_file(p: &Path) -> bool { #[cfg(unix)] fn unlink(p: &Path) -> bool { unsafe { - do p.to_str().as_c_str |buf| { + do p.to_c_str().with_ref |buf| { libc::unlink(buf) == (0 as c_int) } } @@ -1294,7 +1297,7 @@ pub fn glob(pattern: &str) -> ~[Path] { } let mut g = default_glob_t(); - do pattern.as_c_str |c_pattern| { + do pattern.to_c_str().with_ref |c_pattern| { unsafe { libc::glob(c_pattern, 0, ptr::null(), &mut g) } }; do(|| { @@ -1699,6 +1702,7 @@ pub mod consts { #[cfg(test)] mod tests { + use c_str::ToCStr; use libc::{c_int, c_void, size_t}; use libc; use option::Some; @@ -1711,7 +1715,6 @@ mod tests { use rand; use run; use str::StrSlice; - use vec::CopyableVector; use libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; @@ -1941,19 +1944,19 @@ mod tests { let out = tempdir.push("out.txt"); /* Write the temp input file */ - let ostream = do input.to_str().as_c_str |fromp| { - do "w+b".as_c_str |modebuf| { + let ostream = do input.to_c_str().with_ref |fromp| { + do "w+b".to_c_str().with_ref |modebuf| { libc::fopen(fromp, modebuf) } }; assert!((ostream as uint != 0u)); let s = ~"hello"; - let mut buf = s.to_owned().to_c_str(); - let len = buf.len(); - do buf.as_mut_buf |b, _len| { - assert_eq!(libc::fwrite(b as *c_void, 1u as size_t, - (s.len() + 1u) as size_t, ostream), - len as size_t) + do "hello".to_c_str().with_ref |buf| { + let write_len = libc::fwrite(buf as *c_void, + 1u as size_t, + (s.len() + 1u) as size_t, + ostream); + assert_eq!(write_len, (s.len() + 1) as size_t) } assert_eq!(libc::fclose(ostream), (0u as c_int)); let in_mode = input.get_mode(); @@ -2025,11 +2028,11 @@ mod tests { remove_file(&path); let fd = unsafe { - let fd = do path.to_str().as_c_str |path| { + let fd = do path.to_c_str().with_ref |path| { open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR) }; lseek_(fd, size); - do "x".as_c_str |x| { + do "x".to_c_str().with_ref |x| { assert!(write(fd, x as *c_void, 1) == 1); } fd diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 989a5cbd35b3b..fe8776c21d5a3 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -16,9 +16,11 @@ Cross-platform file path handling #[allow(missing_doc)]; +use c_str::ToCStr; +use c_str; use clone::Clone; -use container::Container; use cmp::Eq; +use container::Container; use iterator::{Iterator, IteratorUtil}; use libc; use option::{None, Option, Some}; @@ -341,7 +343,7 @@ mod stat { #[cfg(target_os = "win32")] impl WindowsPath { pub fn stat(&self) -> Option { - do self.to_str().as_c_str |buf| { + do self.to_c_str().with_ref |buf| { let mut st = stat::arch::default_stat(); match unsafe { libc::stat(buf, &mut st) } { 0 => Some(st), @@ -375,7 +377,7 @@ impl WindowsPath { #[cfg(not(target_os = "win32"))] impl PosixPath { pub fn stat(&self) -> Option { - do self.to_str().as_c_str |buf| { + do self.to_c_str().with_ref |buf| { let mut st = stat::arch::default_stat(); match unsafe { libc::stat(buf as *libc::c_char, &mut st) } { 0 => Some(st), @@ -453,7 +455,7 @@ impl PosixPath { #[cfg(unix)] impl PosixPath { pub fn lstat(&self) -> Option { - do self.to_str().as_c_str |buf| { + do self.to_c_str().with_ref |buf| { let mut st = stat::arch::default_stat(); match unsafe { libc::lstat(buf, &mut st) } { 0 => Some(st), @@ -525,6 +527,12 @@ impl ToStr for PosixPath { } } +impl ToCStr for PosixPath { + fn to_c_str(&self) -> c_str::CString { + self.to_str().to_c_str() + } +} + // FIXME (#3227): when default methods in traits are working, de-duplicate // PosixPath and WindowsPath, most of their methods are common. impl GenericPath for PosixPath { @@ -730,6 +738,11 @@ impl ToStr for WindowsPath { } } +impl c_str::ToCStr for WindowsPath { + fn to_c_str(&self) -> c_str::CString { + self.to_str().to_c_str() + } +} impl GenericPath for WindowsPath { fn from_str(s: &str) -> WindowsPath { diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 7e21504c1d294..5b8833f3f361b 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -43,6 +43,7 @@ pub use io::{print, println}; pub use iterator::range; // Reexported types and traits +pub use c_str::ToCStr; pub use clone::{Clone, DeepClone}; pub use cmp::{Eq, ApproxEq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater, Equiv}; pub use char::Char; diff --git a/src/libstd/ptr.rs b/src/libstd/ptr.rs index 3af4769fcdb43..77222490f171d 100644 --- a/src/libstd/ptr.rs +++ b/src/libstd/ptr.rs @@ -486,24 +486,25 @@ pub mod ptr_tests { #[test] fn test_position() { + use c_str::ToCStr; use libc::c_char; - let s = ~"hello"; - unsafe { - assert!(2u == s.as_c_str(|p| position(p, |c| *c == 'l' as c_char))); - assert!(4u == s.as_c_str(|p| position(p, |c| *c == 'o' as c_char))); - assert!(5u == s.as_c_str(|p| position(p, |c| *c == 0 as c_char))); + do "hello".to_c_str().with_ref |p| { + unsafe { + assert!(2u == position(p, |c| *c == 'l' as c_char)); + assert!(4u == position(p, |c| *c == 'o' as c_char)); + assert!(5u == position(p, |c| *c == 0 as c_char)); + } } } #[test] fn test_buf_len() { - let s0 = ~"hello"; - let s1 = ~"there"; - let s2 = ~"thing"; - do s0.as_c_str |p0| { - do s1.as_c_str |p1| { - do s2.as_c_str |p2| { + use c_str::ToCStr; + + do "hello".to_c_str().with_ref |p0| { + do "there".to_c_str().with_ref |p1| { + do "thing".to_c_str().with_ref |p2| { let v = ~[p0, p1, p2, null()]; do v.as_imm_buf |vp, len| { assert_eq!(unsafe { buf_len(vp) }, 3u); diff --git a/src/libstd/rt/borrowck.rs b/src/libstd/rt/borrowck.rs index 4a6a9585b9374..3a51fd032c0e8 100644 --- a/src/libstd/rt/borrowck.rs +++ b/src/libstd/rt/borrowck.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use c_str::ToCStr; use cast::transmute; use libc::{c_char, c_void, size_t, STDERR_FILENO}; use io; @@ -76,7 +77,7 @@ unsafe fn fail_borrowed(box: *mut raw::Box<()>, file: *c_char, line: size_t) { match try_take_task_borrow_list() { None => { // not recording borrows let msg = "borrowed"; - do msg.as_c_str |msg_p| { + do msg.to_c_str().with_ref |msg_p| { sys::begin_unwind_(msg_p, file, line); } } @@ -92,7 +93,7 @@ unsafe fn fail_borrowed(box: *mut raw::Box<()>, file: *c_char, line: size_t) { sep = " and at "; } } - do msg.as_c_str |msg_p| { + do msg.to_c_str().with_ref |msg_p| { sys::begin_unwind_(msg_p, file, line) } } @@ -231,7 +232,7 @@ pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint, let br = borrow_list.pop(); if br.box != a || br.file != file || br.line != line { let err = fmt!("wrong borrow found, br=%?", br); - do err.as_c_str |msg_p| { + do err.to_c_str().with_ref |msg_p| { sys::begin_unwind_(msg_p, file, line) } } diff --git a/src/libstd/rt/logging.rs b/src/libstd/rt/logging.rs index 11d11daebc254..76619704bee43 100644 --- a/src/libstd/rt/logging.rs +++ b/src/libstd/rt/logging.rs @@ -45,15 +45,15 @@ impl Logger for StdErrLogger { /// Configure logging by traversing the crate map and setting the /// per-module global logging flags based on the logging spec pub fn init(crate_map: *u8) { + use c_str::ToCStr; use os; - use str::StrSlice; use ptr; use option::{Some, None}; let log_spec = os::getenv("RUST_LOG"); match log_spec { Some(spec) => { - do spec.as_c_str |buf| { + do spec.to_c_str().with_ref |buf| { unsafe { rust_update_log_settings(crate_map, buf) } } } diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index e15e3adb8c9f3..071a90f4a8346 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -8,26 +8,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use option::*; -use result::*; -use ops::Drop; -use cell::Cell; -use cast; +use c_str::ToCStr; use cast::transmute; +use cast; +use cell::Cell; use clone::Clone; use libc::{c_int, c_uint, c_void}; +use ops::Drop; +use option::*; use ptr; +use result::*; use rt::io::IoError; use rt::io::net::ip::{IpAddr, Ipv4, Ipv6}; -use rt::uv::*; -use rt::uv::idle::IdleWatcher; -use rt::uv::net::{UvIpv4, UvIpv6}; +use rt::io::{standard_error, OtherIoError}; +use rt::local::Local; use rt::rtio::*; use rt::sched::Scheduler; -use rt::io::{standard_error, OtherIoError}; use rt::tube::Tube; -use rt::local::Local; -use str::StrSlice; +use rt::uv::*; +use rt::uv::idle::IdleWatcher; +use rt::uv::net::{UvIpv4, UvIpv6}; use unstable::sync::Exclusive; #[cfg(test)] use container::Container; @@ -663,7 +663,7 @@ impl RtioUdpSocket for UvUdpSocket { }; let r = unsafe { - do ip_str.as_c_str |m_addr| { + do ip_str.to_c_str().with_ref |m_addr| { uvll::udp_set_membership(self.native_handle(), m_addr, ptr::null(), uvll::UV_JOIN_GROUP) } @@ -686,7 +686,7 @@ impl RtioUdpSocket for UvUdpSocket { }; let r = unsafe { - do ip_str.as_c_str |m_addr| { + do ip_str.to_c_str().with_ref |m_addr| { uvll::udp_set_membership(self.native_handle(), m_addr, ptr::null(), uvll::UV_LEAVE_GROUP) } diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index 07264839c3555..bd2d28f8642c4 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -29,6 +29,7 @@ #[allow(non_camel_case_types)]; // C types +use c_str::ToCStr; use libc::{size_t, c_int, c_uint, c_void, c_char, uintptr_t}; use libc::{malloc, free}; use libc; @@ -372,12 +373,12 @@ pub unsafe fn is_ip6_addr(addr: *sockaddr) -> bool { } pub unsafe fn malloc_ip4_addr(ip: &str, port: int) -> *sockaddr_in { - do ip.as_c_str |ip_buf| { + do ip.to_c_str().with_ref |ip_buf| { rust_uv_ip4_addrp(ip_buf as *u8, port as libc::c_int) } } pub unsafe fn malloc_ip6_addr(ip: &str, port: int) -> *sockaddr_in6 { - do ip.as_c_str |ip_buf| { + do ip.to_c_str().with_ref |ip_buf| { rust_uv_ip6_addrp(ip_buf as *u8, port as libc::c_int) } } diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 9114c4879b245..6e447d3ded473 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -12,6 +12,7 @@ #[allow(missing_doc)]; +use c_str::ToCStr; use cast; use clone::Clone; use comm::{stream, SharedChan, GenericChan, GenericPort}; @@ -506,7 +507,7 @@ fn spawn_process_os(prog: &str, args: &[~str], do with_envp(env) |envp| { do with_dirp(dir) |dirp| { - do cmd.as_c_str |cmdp| { + do cmd.to_c_str().with_ref |cmdp| { let created = CreateProcessA(ptr::null(), cast::transmute(cmdp), ptr::mut_null(), ptr::mut_null(), TRUE, 0, envp, dirp, &mut si, &mut pi); @@ -697,17 +698,17 @@ fn with_argv(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T { // hold all the ~[u8] byte strings. let mut tmps = vec::with_capacity(args.len() + 1); - tmps.push(prog.to_owned().to_c_str()); + tmps.push(prog.to_c_str()); foreach arg in args.iter() { - tmps.push(arg.to_owned().to_c_str()); + tmps.push(arg.to_c_str()); } // Next, convert each of the byte strings into a pointer. This is // technically unsafe as the caller could leak these pointers out of our // scope. let mut ptrs = do tmps.map |tmp| { - tmp.as_imm_buf(|buf, _| buf as *libc::c_char) + tmp.with_ref(|buf| buf) }; // Finally, make sure we add a null pointer. @@ -734,7 +735,7 @@ fn with_envp(env: Option<&[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T { // Once again, this is unsafe. let mut ptrs = do tmps.map |tmp| { - tmp.as_imm_buf(|buf, _| buf as *libc::c_char) + tmp.with_ref(|buf| buf) }; ptrs.push(ptr::null()); @@ -757,7 +758,7 @@ fn with_envp(env: Option<&[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T { foreach pair in env.iter() { let kv = fmt!("%s=%s", pair.first(), pair.second()); - blk.push_all(kv.to_c_str()); + blk.push_all(kv.to_c_str().as_bytes()); } blk.push(0); @@ -772,7 +773,7 @@ fn with_envp(env: Option<&[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T { fn with_dirp(d: Option<&Path>, cb: &fn(*libc::c_char) -> T) -> T { match d { - Some(dir) => dir.to_str().as_c_str(cb), + Some(dir) => dir.to_c_str().with_ref(|buf| cb(buf)), None => cb(ptr::null()) } } diff --git a/src/libstd/std.rs b/src/libstd/std.rs index 76d65192e01a9..d51f0de1d275b 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -172,6 +172,7 @@ pub mod local_data; pub mod gc; pub mod libc; +pub mod c_str; pub mod os; pub mod path; pub mod rand; diff --git a/src/libstd/str.rs b/src/libstd/str.rs index f75bbcf20ebcb..851a9326392d1 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -1182,7 +1182,6 @@ pub trait StrSlice<'self> { fn subslice_offset(&self, inner: &str) -> uint; fn as_imm_buf(&self, f: &fn(*u8, uint) -> T) -> T; - fn as_c_str(&self, f: &fn(*libc::c_char) -> T) -> T; } /// Extension methods for strings @@ -1931,32 +1930,6 @@ impl<'self> StrSlice<'self> for &'self str { let v: &[u8] = unsafe { cast::transmute(*self) }; v.as_imm_buf(f) } - - /// Work with the byte buffer of a string as a null-terminated C string. - /// - /// Allows for unsafe manipulation of strings, which is useful for foreign - /// interop. This is similar to `str::as_buf`, but guarantees null-termination. - /// If the given slice is not already null-terminated, this function will - /// allocate a temporary, copy the slice, null terminate it, and pass - /// that instead. - /// - /// # Example - /// - /// ~~~ {.rust} - /// let s = "PATH".as_c_str(|path| libc::getenv(path)); - /// ~~~ - #[inline] - fn as_c_str(&self, f: &fn(*libc::c_char) -> T) -> T { - do self.as_imm_buf |buf, len| { - // NB: len includes the trailing null. - assert!(len > 0); - if unsafe { *(ptr::offset(buf, (len - 1) as int)) != 0 } { - self.to_owned().as_c_str(|s| f(s)) - } else { - f(buf as *libc::c_char) - } - } - } } #[allow(missing_doc)] @@ -1973,13 +1946,6 @@ pub trait OwnedStr { fn capacity(&self) -> uint; fn to_bytes_with_null(self) -> ~[u8]; - /// Allocates a null terminate byte array. - /// - /// # Failure - /// - /// Fails if there are any null characters inside the byte array. - fn to_c_str(self) -> ~[u8]; - /// Work with the mutable byte buffer and length of a slice. /// /// The given length is one byte longer than the 'official' indexable @@ -2171,13 +2137,6 @@ impl OwnedStr for ~str { unsafe { cast::transmute(self) } } - #[inline] - fn to_c_str(self) -> ~[u8] { - let bytes = self.to_bytes_with_null(); - assert!(bytes.slice(0, bytes.len() - 1).iter().all(|byte| *byte != 0)); - bytes - } - #[inline] fn as_mut_buf(&mut self, f: &fn(*mut u8, uint) -> T) -> T { let v: &mut ~[u8] = unsafe { cast::transmute(self) }; @@ -2915,63 +2874,6 @@ mod tests { } } - #[test] - fn test_as_c_str() { - let a = ~""; - do a.as_c_str |buf| { - unsafe { - assert_eq!(*ptr::offset(buf, 0), 0); - } - } - - let a = ~"hello"; - do a.as_c_str |buf| { - unsafe { - assert_eq!(*ptr::offset(buf, 0), 'h' as libc::c_char); - assert_eq!(*ptr::offset(buf, 1), 'e' as libc::c_char); - assert_eq!(*ptr::offset(buf, 2), 'l' as libc::c_char); - assert_eq!(*ptr::offset(buf, 3), 'l' as libc::c_char); - assert_eq!(*ptr::offset(buf, 4), 'o' as libc::c_char); - assert_eq!(*ptr::offset(buf, 5), 0); - } - } - } - - #[test] - fn test_to_c_str() { - let s = ~"ศไทย中华Việt Nam"; - let v = ~[ - 224, 184, 168, 224, 185, 132, 224, 184, 151, 224, 184, 162, 228, - 184, 173, 229, 141, 142, 86, 105, 225, 187, 135, 116, 32, 78, 97, - 109, 0 - ]; - assert_eq!((~"").to_c_str(), ~[0]); - assert_eq!((~"abc").to_c_str(), ~['a' as u8, 'b' as u8, 'c' as u8, 0]); - assert_eq!(s.to_c_str(), v); - } - - #[test] - fn test_as_c_str() { - let a = ~""; - do a.as_c_str |buf| { - unsafe { - assert_eq!(*ptr::offset(buf, 0), 0); - } - } - - let a = ~"hello"; - do a.as_c_str |buf| { - unsafe { - assert_eq!(*ptr::offset(buf, 0), 'h' as libc::c_char); - assert_eq!(*ptr::offset(buf, 1), 'e' as libc::c_char); - assert_eq!(*ptr::offset(buf, 2), 'l' as libc::c_char); - assert_eq!(*ptr::offset(buf, 3), 'l' as libc::c_char); - assert_eq!(*ptr::offset(buf, 4), 'o' as libc::c_char); - assert_eq!(*ptr::offset(buf, 5), 0); - } - } - } - #[test] fn test_subslice_offset() { let a = "kernelsprite"; diff --git a/src/libstd/sys.rs b/src/libstd/sys.rs index 51609709cdbca..b40f87617a914 100644 --- a/src/libstd/sys.rs +++ b/src/libstd/sys.rs @@ -12,13 +12,13 @@ #[allow(missing_doc)]; +use c_str::ToCStr; use cast; use gc; use io; use libc; use libc::{c_char, size_t}; use repr; -use str::StrSlice; use str; use unstable::intrinsics; @@ -115,8 +115,8 @@ pub trait FailWithCause { impl FailWithCause for ~str { fn fail_with(cause: ~str, file: &'static str, line: uint) -> ! { - do cause.as_c_str |msg_buf| { - do file.as_c_str |file_buf| { + do cause.to_c_str().with_ref |msg_buf| { + do file.to_c_str().with_ref |file_buf| { begin_unwind_(msg_buf, file_buf, line as libc::size_t) } } @@ -125,8 +125,8 @@ impl FailWithCause for ~str { impl FailWithCause for &'static str { fn fail_with(cause: &'static str, file: &'static str, line: uint) -> ! { - do cause.as_c_str |msg_buf| { - do file.as_c_str |file_buf| { + do cause.to_c_str().with_ref |msg_buf| { + do file.to_c_str().with_ref |file_buf| { begin_unwind_(msg_buf, file_buf, line as libc::size_t) } } diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs index 8d5654255f14a..49e3e0777df84 100644 --- a/src/libstd/unstable/dynamic_lib.rs +++ b/src/libstd/unstable/dynamic_lib.rs @@ -15,6 +15,7 @@ Dynamic library facilities. A simple wrapper over the platforms dynamic library facilities */ +use c_str::ToCStr; use cast; use path; use libc; @@ -65,7 +66,7 @@ impl DynamicLibrary { // T but that feature is still unimplemented let maybe_symbol_value = do dl::check_for_errors_in { - do symbol.as_c_str |raw_string| { + do symbol.to_c_str().with_ref |raw_string| { dl::symbol(self.handle, raw_string) } }; @@ -135,6 +136,7 @@ mod test { #[cfg(target_os = "macos")] #[cfg(target_os = "freebsd")] mod dl { + use c_str::ToCStr; use libc; use path; use ptr; @@ -143,7 +145,7 @@ mod dl { use result::*; pub unsafe fn open_external(filename: &path::Path) -> *libc::c_void { - do filename.to_str().as_c_str |raw_name| { + do filename.to_c_str().with_ref |raw_name| { dlopen(raw_name, Lazy as libc::c_int) } } diff --git a/src/libstd/unstable/lang.rs b/src/libstd/unstable/lang.rs index e0c4950b38eaa..1ea815f721cc1 100644 --- a/src/libstd/unstable/lang.rs +++ b/src/libstd/unstable/lang.rs @@ -10,6 +10,7 @@ //! Runtime calls emitted by the compiler. +use c_str::ToCStr; use cast::transmute; use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int}; use str; @@ -49,7 +50,7 @@ pub fn fail_bounds_check(file: *c_char, line: size_t, index: size_t, len: size_t) { let msg = fmt!("index out of bounds: the len is %d but the index is %d", len as int, index as int); - do msg.as_c_str |buf| { + do msg.to_c_str().with_ref |buf| { fail_(buf, file, line); } } diff --git a/src/test/run-pass/foreign-fn-linkname.rs b/src/test/run-pass/foreign-fn-linkname.rs index 38f36dd258b7a..b5a114ef22364 100644 --- a/src/test/run-pass/foreign-fn-linkname.rs +++ b/src/test/run-pass/foreign-fn-linkname.rs @@ -26,8 +26,9 @@ mod libc { fn strlen(str: ~str) -> uint { unsafe { // C string is terminated with a zero - let bytes = str.to_bytes_with_null(); - return libc::my_strlen(vec::raw::to_ptr(bytes)); + do str.to_c_str().with_ref |buf| { + libc::my_strlen(buf as *u8) + } } } From bd908d4c0e9cc4717928e63d7ed64cd9ffe0bfce Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 7 Jul 2013 13:30:48 -0700 Subject: [PATCH 10/26] std and rustc: explicitly pass c strings to c functions When strings lose their trailing null, this pattern will become dangerous: let foo = "bar"; let foo_ptr: *u8 = &foo[0]; Instead we should use c_strs to handle this correctly. --- src/librustc/middle/trans/base.rs | 9 ++- src/libstd/libc.rs | 4 +- src/libstd/os.rs | 94 ++++++++++++++------------- src/libstd/ptr.rs | 101 ++++++++++++++++-------------- 4 files changed, 112 insertions(+), 96 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 5b048558fd117..bfcc1e1b6a168 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2400,11 +2400,10 @@ pub fn create_entry_wrapper(ccx: @mut CrateContext, (rust_main, args) }; - let result = llvm::LLVMBuildCall(bld, - start_fn, - &args[0], - args.len() as c_uint, - noname()); + let result = do args.as_imm_buf |buf, len| { + llvm::LLVMBuildCall(bld, start_fn, buf, len as c_uint, noname()) + }; + llvm::LLVMBuildRet(bld, result); } } diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs index f96d3ce263ec1..678704fe09833 100644 --- a/src/libstd/libc.rs +++ b/src/libstd/libc.rs @@ -2652,7 +2652,7 @@ pub mod funcs { pub fn execvpe(c: *c_char, argv: **c_char, envp: **c_char) -> c_int; #[link_name = "_getcwd"] - pub fn getcwd(buf: *c_char, size: size_t) -> *c_char; + pub fn getcwd(buf: *mut c_char, size: size_t) -> *c_char; #[link_name = "_getpid"] pub fn getpid() -> c_int; #[link_name = "_isatty"] @@ -2804,7 +2804,7 @@ pub mod funcs { pub fn execvp(c: *c_char, argv: **c_char) -> c_int; pub fn fork() -> pid_t; pub fn fpathconf(filedes: c_int, name: c_int) -> c_long; - pub fn getcwd(buf: *c_char, size: size_t) -> *c_char; + pub fn getcwd(buf: *mut c_char, size: size_t) -> *c_char; pub fn getegid() -> gid_t; pub fn geteuid() -> uid_t; pub fn getgid() -> gid_t ; diff --git a/src/libstd/os.rs b/src/libstd/os.rs index fb4f14d33c60b..48a14d6893ed0 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -74,14 +74,15 @@ pub static TMPBUF_SZ : uint = 1000u; static BUF_BYTES : uint = 2048u; pub fn getcwd() -> Path { - let buf = [0 as libc::c_char, ..BUF_BYTES]; - unsafe { - if(0 as *libc::c_char == libc::getcwd( - &buf[0], - BUF_BYTES as libc::size_t)) { - fail!(); + let mut buf = [0 as libc::c_char, ..BUF_BYTES]; + do buf.as_mut_buf |buf, len| { + unsafe { + if libc::getcwd(buf, len as size_t).is_null() { + fail!() + } + + Path(str::raw::from_c_str(buf as *c_char)) } - Path(str::raw::from_c_str(&buf[0])) } } @@ -464,18 +465,18 @@ pub fn self_exe_path() -> Option { unsafe { use libc::funcs::posix01::unistd::readlink; - let mut path_str = str::with_capacity(TMPBUF_SZ); - let len = do path_str.to_c_str().with_ref |buf| { - let buf = buf as *mut c_char; - do "/proc/self/exe".to_c_str().with_ref |proc_self_buf| { - readlink(proc_self_buf, buf, TMPBUF_SZ as size_t) + let mut path = [0 as c_char, .. TMPBUF_SZ]; + + do path.as_mut_buf |buf, len| { + let len = do "/proc/self/exe".to_c_str.with_ref |proc_self_buf| { + readlink(proc_self_buf, buf, len as size_t) as uint + }; + + if len == -1 { + None + } else { + Some(str::raw::from_buf_len(buf as *u8, len)) } - }; - if len == -1 { - None - } else { - str::raw::set_len(&mut path_str, len as uint); - Some(path_str) } } } @@ -699,13 +700,15 @@ pub fn list_dir(p: &Path) -> ~[~str] { extern { fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; } - let input = p.to_str(); let mut strings = ~[]; - let input_ptr = ::cast::transmute(&input[0]); debug!("os::list_dir -- BEFORE OPENDIR"); - let dir_ptr = opendir(input_ptr); + + let dir_ptr = do p.to_c_str().with_ref |buf| { + opendir(buf) + }; + if (dir_ptr as uint != 0) { - debug!("os::list_dir -- opendir() SUCCESS"); + debug!("os::list_dir -- opendir() SUCCESS"); let mut entry_ptr = readdir(dir_ptr); while (entry_ptr as uint != 0) { strings.push(str::raw::from_c_str(rust_list_dir_val( @@ -715,7 +718,7 @@ pub fn list_dir(p: &Path) -> ~[~str] { closedir(dir_ptr); } else { - debug!("os::list_dir -- opendir() FAILURE"); + debug!("os::list_dir -- opendir() FAILURE"); } debug!( "os::list_dir -- AFTER -- #: %?", @@ -1043,14 +1046,15 @@ pub fn last_os_error() -> ~str { } let mut buf = [0 as c_char, ..TMPBUF_SZ]; - unsafe { - let err = strerror_r(errno() as c_int, &mut buf[0], - TMPBUF_SZ as size_t); - if err < 0 { - fail!("strerror_r failure"); - } - str::raw::from_c_str(&buf[0]) + do buf.as_mut_buf |buf, len| { + unsafe { + if strerror_r(errno() as c_int, buf, len as size_t) < 0 { + fail!("strerror_r failure"); + } + + str::raw::from_c_str(buf as *c_char) + } } } @@ -1076,23 +1080,29 @@ pub fn last_os_error() -> ~str { static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000; static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200; - let mut buf = [0 as c_char, ..TMPBUF_SZ]; - // This value is calculated from the macro // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) let langId = 0x0800 as DWORD; let err = errno() as DWORD; - unsafe { - let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - ptr::mut_null(), err, langId, - &mut buf[0], TMPBUF_SZ as DWORD, - ptr::null()); - if res == 0 { - fail!("[%?] FormatMessage failure", errno()); - } - str::raw::from_c_str(&buf[0]) + let mut buf = [0 as c_char, ..TMPBUF_SZ]; + + do buf.as_imm_buf |buf, len| { + unsafe { + let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + ptr::mut_null(), + err, + langId, + buf, + len as DWORD, + ptr::null()); + if res == 0 { + fail!("[%?] FormatMessage failure", errno()); + } + + str::raw::from_c_str(buf) + } } } diff --git a/src/libstd/ptr.rs b/src/libstd/ptr.rs index 77222490f171d..88a915a1e5594 100644 --- a/src/libstd/ptr.rs +++ b/src/libstd/ptr.rs @@ -442,6 +442,7 @@ pub mod ptr_tests { use super::*; use prelude::*; + use c_str::ToCStr; use cast; use libc; use str; @@ -486,7 +487,6 @@ pub mod ptr_tests { #[test] fn test_position() { - use c_str::ToCStr; use libc::c_char; do "hello".to_c_str().with_ref |p| { @@ -500,8 +500,6 @@ pub mod ptr_tests { #[test] fn test_buf_len() { - use c_str::ToCStr; - do "hello".to_c_str().with_ref |p0| { do "there".to_c_str().with_ref |p1| { do "thing".to_c_str().with_ref |p2| { @@ -608,66 +606,75 @@ pub mod ptr_tests { #[test] fn test_ptr_array_each_with_len() { unsafe { - let one = ~"oneOne"; - let two = ~"twoTwo"; - let three = ~"threeThree"; - let arr: ~[*i8] = ~[ - ::cast::transmute(&one[0]), - ::cast::transmute(&two[0]), - ::cast::transmute(&three[0]), + let one = "oneOne".to_c_str(); + let two = "twoTwo".to_c_str(); + let three = "threeThree".to_c_str(); + let arr = ~[ + one.with_ref(|buf| buf), + two.with_ref(|buf| buf), + three.with_ref(|buf| buf), ]; let expected_arr = [ one, two, three ]; - let arr_ptr = &arr[0]; - let mut ctr = 0; - let mut iteration_count = 0; - array_each_with_len(arr_ptr, arr.len(), - |e| { - let actual = str::raw::from_c_str(e); - let expected = expected_arr[ctr].clone(); - debug!( - "test_ptr_array_each e: %s, a: %s", - expected, actual); - assert_eq!(actual, expected); - ctr += 1; - iteration_count += 1; - }); - assert_eq!(iteration_count, 3u); + + do arr.as_imm_buf |arr_ptr, arr_len| { + let mut ctr = 0; + let mut iteration_count = 0; + do array_each_with_len(arr_ptr, arr_len) |e| { + let actual = str::raw::from_c_str(e); + let expected = do expected_arr[ctr].with_ref |buf| { + str::raw::from_c_str(buf) + }; + debug!( + "test_ptr_array_each_with_len e: %s, a: %s", + expected, actual); + assert_eq!(actual, expected); + ctr += 1; + iteration_count += 1; + } + assert_eq!(iteration_count, 3u); + } } } + #[test] fn test_ptr_array_each() { unsafe { - let one = ~"oneOne"; - let two = ~"twoTwo"; - let three = ~"threeThree"; - let arr: ~[*i8] = ~[ - ::cast::transmute(&one[0]), - ::cast::transmute(&two[0]), - ::cast::transmute(&three[0]), + let one = "oneOne".to_c_str(); + let two = "twoTwo".to_c_str(); + let three = "threeThree".to_c_str(); + let arr = ~[ + one.with_ref(|buf| buf), + two.with_ref(|buf| buf), + three.with_ref(|buf| buf), // fake a null terminator - 0 as *i8 + null(), ]; let expected_arr = [ one, two, three ]; - let arr_ptr = &arr[0]; - let mut ctr = 0; - let mut iteration_count = 0; - array_each(arr_ptr, |e| { - let actual = str::raw::from_c_str(e); - let expected = expected_arr[ctr].clone(); - debug!( - "test_ptr_array_each e: %s, a: %s", - expected, actual); - assert_eq!(actual, expected); - ctr += 1; - iteration_count += 1; - }); - assert_eq!(iteration_count, 3); + + do arr.as_imm_buf |arr_ptr, arr_len| { + let mut ctr = 0; + let mut iteration_count = 0; + do array_each(arr_ptr) |e| { + let actual = str::raw::from_c_str(e); + let expected = do expected_arr[ctr].with_ref |buf| { + str::raw::from_c_str(buf) + }; + debug!( + "test_ptr_array_each e: %s, a: %s", + expected, actual); + assert_eq!(actual, expected); + ctr += 1; + iteration_count += 1; + } + assert_eq!(iteration_count, 3); + } } } + #[test] #[should_fail] #[ignore(cfg(windows))] From 3629f702e91ab1b34a556ecc34c6a5c89e0aae0c Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 4 Aug 2013 14:08:20 -0700 Subject: [PATCH 11/26] std: merge str::raw::from_buf and str::raw::from_c_str --- src/libstd/os.rs | 2 +- src/libstd/str.rs | 34 +++++++++++------------------- src/test/run-pass/const-str-ptr.rs | 23 ++++++++++++-------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 48a14d6893ed0..7a7bcc3efa3b3 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -93,7 +93,7 @@ pub fn fill_charp_buf(f: &fn(*mut c_char, size_t) -> bool) -> Option<~str> { do buf.as_mut_buf |b, sz| { if f(b, sz as size_t) { unsafe { - Some(str::raw::from_buf(b as *u8)) + Some(str::raw::from_c_str(b as *c_char)) } } else { None diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 851a9326392d1..4935477536fc5 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -763,17 +763,6 @@ pub mod raw { use vec::MutableVector; use unstable::raw::{Slice, String}; - /// Create a Rust string from a null-terminated *u8 buffer - pub unsafe fn from_buf(buf: *u8) -> ~str { - let mut curr = buf; - let mut i = 0u; - while *curr != 0u8 { - i += 1u; - curr = ptr::offset(buf, i as int); - } - return from_buf_len(buf, i); - } - /// Create a Rust string from a *u8 buffer of the given length pub unsafe fn from_buf_len(buf: *u8, len: uint) -> ~str { let mut v: ~[u8] = vec::with_capacity(len + 1); @@ -784,17 +773,18 @@ pub mod raw { v.push(0u8); assert!(is_utf8(v)); - return cast::transmute(v); + cast::transmute(v) } /// Create a Rust string from a null-terminated C string - pub unsafe fn from_c_str(c_str: *libc::c_char) -> ~str { - from_buf(c_str as *u8) - } - - /// Create a Rust string from a `*c_char` buffer of the given length - pub unsafe fn from_c_str_len(c_str: *libc::c_char, len: uint) -> ~str { - from_buf_len(c_str as *u8, len) + pub unsafe fn from_c_str(buf: *libc::c_char) -> ~str { + let mut curr = buf; + let mut i = 0; + while *curr != 0 { + i += 1; + curr = ptr::offset(buf, i); + } + from_buf_len(buf as *u8, i as uint) } /// Converts a vector of bytes to a new owned string. @@ -2805,11 +2795,11 @@ mod tests { } #[test] - fn test_from_buf() { + fn test_raw_from_c_str() { unsafe { - let a = ~[65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 0u8]; + let a = ~[65, 65, 65, 65, 65, 65, 65, 0]; let b = vec::raw::to_ptr(a); - let c = raw::from_buf(b); + let c = raw::from_c_str(b); assert_eq!(c, ~"AAAAAAA"); } } diff --git a/src/test/run-pass/const-str-ptr.rs b/src/test/run-pass/const-str-ptr.rs index 2f0cd3c611f62..0e7d6d9f16a3c 100644 --- a/src/test/run-pass/const-str-ptr.rs +++ b/src/test/run-pass/const-str-ptr.rs @@ -10,15 +10,20 @@ use std::str; -static a: [u8, ..3] = ['h' as u8, 'i' as u8, 0 as u8]; -static c: &'static [u8, ..3] = &a; -static b: *u8 = c as *u8; +static A: [u8, ..2] = ['h' as u8, 'i' as u8]; +static B: &'static [u8, ..2] = &A; +static C: *u8 = B as *u8; pub fn main() { - let foo = &a as *u8; - assert_eq!(unsafe { str::raw::from_bytes(a) }, ~"hi\x00"); - assert_eq!(unsafe { str::raw::from_buf(foo) }, ~"hi"); - assert_eq!(unsafe { str::raw::from_buf(b) }, ~"hi"); - assert!(unsafe { *b == a[0] }); - assert!(unsafe { *(&c[0] as *u8) == a[0] }); + unsafe { + let foo = &A as *u8; + assert_eq!(str::raw::from_bytes(A), ~"hi"); + assert_eq!(str::raw::from_buf_len(foo, A.len()), ~"hi"); + assert_eq!(str::raw::from_buf_len(C, B.len()), ~"hi"); + assert!(*C == A[0]); + assert!(*(&B[0] as *u8) == A[0]); + + let bar = str::raw::from_bytes(A).to_c_str(); + assert_eq!(bar.with_ref(|buf| str::raw::from_c_str(buf)), ~"hi"); + } } From 17e0089856de145b10485c9a96bcf4f39e5b7f71 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 7 Jul 2013 09:42:38 -0700 Subject: [PATCH 12/26] std: remove use of cast module from os. --- src/libstd/os.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 7a7bcc3efa3b3..b6c9979cc7d3d 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -29,7 +29,6 @@ #[allow(missing_doc)]; use c_str::ToCStr; -use cast; use clone::Clone; use container::Container; use io; @@ -246,10 +245,10 @@ pub fn getenv(n: &str) -> Option<~str> { let s = do n.to_c_str().with_ref |buf| { libc::getenv(buf) }; - if ptr::null::() == cast::transmute(s) { + if s.is_null() { None } else { - Some(str::raw::from_buf(cast::transmute(s))) + Some(str::raw::from_c_str(s)) } } } @@ -643,8 +642,7 @@ pub fn make_dir(p: &Path, mode: c_int) -> bool { use os::win32::as_utf16_p; // FIXME: turn mode into something useful? #2623 do as_utf16_p(p.to_str()) |buf| { - libc::CreateDirectoryW(buf, cast::transmute(0)) - != (0 as libc::BOOL) + libc::CreateDirectoryW(buf, ptr::null()) != (0 as libc::BOOL) } } } @@ -748,10 +746,7 @@ pub fn list_dir(p: &Path) -> ~[~str] { do as_utf16_p(star(p).to_str()) |path_ptr| { let mut strings = ~[]; let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); - let find_handle = - FindFirstFileW( - path_ptr, - ::cast::transmute(wfd_ptr)); + let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE); if find_handle as libc::c_int != INVALID_HANDLE_VALUE { let mut more_files = 1 as libc::c_int; while more_files != 0 { @@ -765,9 +760,7 @@ pub fn list_dir(p: &Path) -> ~[~str] { let fp_str = str::from_utf16(fp_vec); strings.push(fp_str); } - more_files = FindNextFileW( - find_handle, - ::cast::transmute(wfd_ptr)); + more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); } FindClose(find_handle); free(wfd_ptr) @@ -1195,7 +1188,7 @@ pub fn real_args() -> ~[~str] { } unsafe { - LocalFree(cast::transmute(szArgList)); + LocalFree(szArgList as *c_void); } return args; From 5865a7597b060e81a6540b5d205b4a7d45a9b328 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 4 Aug 2013 13:22:56 -0700 Subject: [PATCH 13/26] Remove trailing null from strings --- src/libextra/terminfo/parm.rs | 100 ++++++- src/librustc/middle/trans/common.rs | 2 +- src/librustc/middle/trans/expr.rs | 7 - src/librustc/middle/trans/reflect.rs | 2 +- src/librustc/middle/trans/tvec.rs | 7 +- src/libstd/cast.rs | 10 + src/libstd/io.rs | 6 + src/libstd/run.rs | 3 +- src/libstd/str.rs | 384 ++++++++++++++++++++++++++- src/libstd/str/ascii.rs | 47 +++- src/libstd/unstable/extfmt.rs | 14 +- src/rt/rust_builtin.cpp | 5 +- 12 files changed, 544 insertions(+), 43 deletions(-) diff --git a/src/libextra/terminfo/parm.rs b/src/libextra/terminfo/parm.rs index 3669c1ea0a357..d95e5d8d6d8e3 100644 --- a/src/libextra/terminfo/parm.rs +++ b/src/libextra/terminfo/parm.rs @@ -476,6 +476,7 @@ impl FormatOp { } } +#[cfg(stage0)] priv fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> { let mut s = match val { Number(d) => { @@ -545,8 +546,103 @@ priv fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> { String(s) => { match op { FormatString => { - let mut s = s.to_bytes_with_null(); - s.pop(); // remove the null + let mut s = s.as_bytes().to_owned(); + if flags.precision > 0 && flags.precision < s.len() { + s.truncate(flags.precision); + } + s + } + _ => { + return Err(fmt!("non-string on stack with %%%c", op.to_char())) + } + } + } + }; + if flags.width > s.len() { + let n = flags.width - s.len(); + if flags.left { + s.grow(n, &(' ' as u8)); + } else { + let mut s_ = vec::with_capacity(flags.width); + s_.grow(n, &(' ' as u8)); + s_.push_all_move(s); + s = s_; + } + } + Ok(s) +} + +#[cfg(not(stage0))] +priv fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> { + let mut s = match val { + Number(d) => { + match op { + FormatString => { + return Err(~"non-number on stack with %s") + } + _ => { + let radix = match op { + FormatDigit => 10, + FormatOctal => 8, + FormatHex|FormatHEX => 16, + FormatString => util::unreachable() + }; + let mut s = ~[]; + match op { + FormatDigit => { + let sign = if flags.sign { SignAll } else { SignNeg }; + do int_to_str_bytes_common(d, radix, sign) |c| { + s.push(c); + } + } + _ => { + do int_to_str_bytes_common(d as uint, radix, SignNone) |c| { + s.push(c); + } + } + }; + if flags.precision > s.len() { + let mut s_ = vec::with_capacity(flags.precision); + let n = flags.precision - s.len(); + s_.grow(n, &('0' as u8)); + s_.push_all_move(s); + s = s_; + } + assert!(!s.is_empty(), "string conversion produced empty result"); + match op { + FormatDigit => { + if flags.space && !(s[0] == '-' as u8 || s[0] == '+' as u8) { + s.unshift(' ' as u8); + } + } + FormatOctal => { + if flags.alternate && s[0] != '0' as u8 { + s.unshift('0' as u8); + } + } + FormatHex => { + if flags.alternate { + let s_ = util::replace(&mut s, ~['0' as u8, 'x' as u8]); + s.push_all_move(s_); + } + } + FormatHEX => { + s = s.into_ascii().to_upper().into_bytes(); + if flags.alternate { + let s_ = util::replace(&mut s, ~['0' as u8, 'X' as u8]); + s.push_all_move(s_); + } + } + FormatString => util::unreachable() + } + s + } + } + } + String(s) => { + match op { + FormatString => { + let mut s = s.as_bytes().to_owned(); if flags.precision > 0 && flags.precision < s.len() { s.truncate(flags.precision); } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 4f5fae8db1a4c..2bd1b34f84c69 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -780,7 +780,7 @@ pub fn C_estr_slice(cx: &mut CrateContext, s: @str) -> ValueRef { unsafe { let len = s.len(); let cs = llvm::LLVMConstPointerCast(C_cstr(cx, s), Type::i8p().to_ref()); - C_struct([cs, C_uint(cx, len + 1u /* +1 for null */)]) + C_struct([cs, C_uint(cx, len)]) } } diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 10586dbe55b1a..2487d481c0dea 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -870,7 +870,6 @@ fn trans_lvalue_unadjusted(bcx: @mut Block, expr: @ast::expr) -> DatumBlock { let _icx = push_ctxt("trans_index"); let ccx = bcx.ccx(); - let base_ty = expr_ty(bcx, base); let mut bcx = bcx; let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base)); @@ -900,12 +899,6 @@ fn trans_lvalue_unadjusted(bcx: @mut Block, expr: @ast::expr) -> DatumBlock { let (bcx, base, len) = base_datum.get_vec_base_and_len(bcx, index_expr.span, index_expr.id, 0); - let mut len = len; - - if ty::type_is_str(base_ty) { - // acccount for null terminator in the case of string - len = Sub(bcx, len, C_uint(bcx.ccx(), 1u)); - } debug!("trans_index: base %s", bcx.val_to_str(base)); debug!("trans_index: len %s", bcx.val_to_str(len)); diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 93b2e8a6665df..032bbd1be3a84 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -58,7 +58,7 @@ impl Reflector { let str_vstore = ty::vstore_slice(ty::re_static); let str_ty = ty::mk_estr(bcx.tcx(), str_vstore); let scratch = scratch_datum(bcx, str_ty, "", false); - let len = C_uint(bcx.ccx(), s.len() + 1); + let len = C_uint(bcx.ccx(), s.len()); let c_str = PointerCast(bcx, C_cstr(bcx.ccx(), s), Type::i8p()); Store(bcx, c_str, GEPi(bcx, scratch.val, [ 0, 0 ])); Store(bcx, len, GEPi(bcx, scratch.val, [ 0, 1 ])); diff --git a/src/librustc/middle/trans/tvec.rs b/src/librustc/middle/trans/tvec.rs index b37a99771bde0..58a242c53ec1f 100644 --- a/src/librustc/middle/trans/tvec.rs +++ b/src/librustc/middle/trans/tvec.rs @@ -265,7 +265,7 @@ pub fn trans_lit_str(bcx: @mut Block, Ignore => bcx, SaveIn(lldest) => { unsafe { - let bytes = str_lit.len() + 1; // count null-terminator too + let bytes = str_lit.len(); // count null-terminator too let llbytes = C_uint(bcx.ccx(), bytes); let llcstr = C_cstr(bcx.ccx(), str_lit); let llcstr = llvm::LLVMConstPointerCast(llcstr, Type::i8p().to_ref()); @@ -363,7 +363,7 @@ pub fn write_content(bcx: @mut Block, return bcx; } SaveIn(lldest) => { - let bytes = s.len() + 1; // copy null-terminator too + let bytes = s.len(); let llbytes = C_uint(bcx.ccx(), bytes); let llcstr = C_cstr(bcx.ccx(), s); base::call_memcpy(bcx, lldest, llcstr, llbytes, 1); @@ -491,7 +491,7 @@ pub fn elements_required(bcx: @mut Block, content_expr: &ast::expr) -> uint { match content_expr.node { ast::expr_lit(@codemap::spanned { node: ast::lit_str(s), _ }) => { - s.len() + 1 + s.len() }, ast::expr_vec(ref es, _) => es.len(), ast::expr_repeat(_, count_expr, _) => { @@ -524,7 +524,6 @@ pub fn get_base_and_len(bcx: @mut Block, match vstore { ty::vstore_fixed(n) => { let base = GEPi(bcx, llval, [0u, 0u]); - let n = if ty::type_is_str(vec_ty) { n + 1u } else { n }; let len = Mul(bcx, C_uint(ccx, n), vt.llunit_size); (base, len) } diff --git a/src/libstd/cast.rs b/src/libstd/cast.rs index ee91d12790953..ff9057afb55fc 100644 --- a/src/libstd/cast.rs +++ b/src/libstd/cast.rs @@ -165,10 +165,20 @@ mod tests { } } + #[cfg(stage0)] #[test] fn test_transmute2() { unsafe { assert_eq!(~[76u8, 0u8], transmute(~"L")); } } + + #[cfg(not(stage0))] + #[test] + fn test_transmute2() { + unsafe { + assert_eq!(~[76u8], transmute(~"L")); + } + } + } diff --git a/src/libstd/io.rs b/src/libstd/io.rs index 78c6e8d5342de..f0d4c3a737596 100644 --- a/src/libstd/io.rs +++ b/src/libstd/io.rs @@ -1707,6 +1707,7 @@ pub fn with_bytes_writer(f: &fn(@Writer)) -> ~[u8] { (*bytes).clone() } +#[cfg(stage0)] pub fn with_str_writer(f: &fn(@Writer)) -> ~str { let mut v = with_bytes_writer(f); @@ -1719,6 +1720,11 @@ pub fn with_str_writer(f: &fn(@Writer)) -> ~str { } } +#[cfg(not(stage0))] +pub fn with_str_writer(f: &fn(@Writer)) -> ~str { + str::from_bytes(with_bytes_writer(f)) +} + // Utility functions pub fn seek_in_buf(offset: int, pos: uint, len: uint, whence: SeekStyle) -> uint { diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 6e447d3ded473..d0f7f307088c4 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -758,7 +758,8 @@ fn with_envp(env: Option<&[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T { foreach pair in env.iter() { let kv = fmt!("%s=%s", pair.first(), pair.second()); - blk.push_all(kv.to_c_str().as_bytes()); + blk.push_all(kv.as_bytes()); + blk.push(0); } blk.push(0); diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 4935477536fc5..5796b541186e5 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -33,6 +33,7 @@ use ptr; use ptr::RawPtr; use to_str::ToStr; use uint; +#[cfg(stage0)] use unstable::raw::Repr; use vec; use vec::{OwnedVector, OwnedCopyableVector, ImmutableVector, MutableVector}; @@ -91,6 +92,7 @@ pub fn from_bytes_owned(vv: ~[u8]) -> ~str { /// # Failure /// /// Fails if invalid UTF-8 +#[cfg(stage0)] pub fn from_bytes_slice<'a>(vector: &'a [u8]) -> &'a str { unsafe { assert!(is_utf8(vector)); @@ -100,6 +102,20 @@ pub fn from_bytes_slice<'a>(vector: &'a [u8]) -> &'a str { } } +/// Converts a vector to a string slice without performing any allocations. +/// +/// Once the slice has been validated as utf-8, it is transmuted in-place and +/// returned as a '&str' instead of a '&[u8]' +/// +/// # Failure +/// +/// Fails if invalid UTF-8 +#[cfg(not(stage0))] +pub fn from_bytes_slice<'a>(v: &'a [u8]) -> &'a str { + assert!(is_utf8(v)); + unsafe { cast::transmute(v) } +} + impl ToStr for ~str { #[inline] fn to_str(&self) -> ~str { self.to_owned() } @@ -118,11 +134,23 @@ impl ToStr for @str { /// # Failure /// /// Fails if invalid UTF-8 +#[cfg(stage0)] pub fn from_byte(b: u8) -> ~str { assert!(b < 128u8); unsafe { cast::transmute(~[b, 0u8]) } } +/// Convert a byte to a UTF-8 string +/// +/// # Failure +/// +/// Fails if invalid UTF-8 +#[cfg(not(stage0))] +pub fn from_byte(b: u8) -> ~str { + assert!(b < 128u8); + unsafe { ::cast::transmute(~[b]) } +} + /// Convert a char to a string pub fn from_char(ch: char) -> ~str { let mut buf = ~""; @@ -153,6 +181,7 @@ pub trait StrVector { impl<'self, S: Str> StrVector for &'self [S] { /// Concatenate a vector of strings. + #[cfg(stage0)] pub fn concat(&self) -> ~str { if self.is_empty() { return ~""; } @@ -176,7 +205,32 @@ impl<'self, S: Str> StrVector for &'self [S] { s } + /// Concatenate a vector of strings. + #[cfg(not(stage0))] + pub fn concat(&self) -> ~str { + if self.is_empty() { return ~""; } + + let len = self.iter().transform(|s| s.as_slice().len()).sum(); + + let mut s = with_capacity(len); + + unsafe { + do s.as_mut_buf |buf, _| { + let mut buf = buf; + foreach ss in self.iter() { + do ss.as_slice().as_imm_buf |ssbuf, sslen| { + ptr::copy_memory(buf, ssbuf, sslen); + buf = buf.offset(sslen as int); + } + } + } + raw::set_len(&mut s, len); + } + s + } + /// Concatenate a vector of strings, placing a given separator between each. + #[cfg(stage0)] pub fn connect(&self, sep: &str) -> ~str { if self.is_empty() { return ~""; } @@ -215,6 +269,45 @@ impl<'self, S: Str> StrVector for &'self [S] { } s } + + /// Concatenate a vector of strings, placing a given separator between each. + #[cfg(not(stage0))] + pub fn connect(&self, sep: &str) -> ~str { + if self.is_empty() { return ~""; } + + // concat is faster + if sep.is_empty() { return self.concat(); } + + // this is wrong without the guarantee that `self` is non-empty + let len = sep.len() * (self.len() - 1) + + self.iter().transform(|s| s.as_slice().len()).sum(); + let mut s = ~""; + let mut first = true; + + s.reserve(len); + + unsafe { + do s.as_mut_buf |buf, _| { + do sep.as_imm_buf |sepbuf, seplen| { + let mut buf = buf; + foreach ss in self.iter() { + do ss.as_slice().as_imm_buf |ssbuf, sslen| { + if first { + first = false; + } else { + ptr::copy_memory(buf, sepbuf, seplen); + buf = buf.offset(seplen as int); + } + ptr::copy_memory(buf, ssbuf, sslen); + buf = buf.offset(sslen as int); + } + } + } + } + raw::set_len(&mut s, len); + } + s + } } /// Something that can be used to compare against a character @@ -485,7 +578,7 @@ Section: Comparing strings */ /// Bytewise slice equality -#[cfg(not(test))] +#[cfg(not(test), stage0)] #[lang="str_eq"] #[inline] pub fn eq_slice(a: &str, b: &str) -> bool { @@ -503,7 +596,28 @@ pub fn eq_slice(a: &str, b: &str) -> bool { } } -#[cfg(test)] +/// Bytewise slice equality +#[cfg(not(test), not(stage0))] +#[lang="str_eq"] +#[inline] +pub fn eq_slice(a: &str, b: &str) -> bool { + do a.as_imm_buf |ap, alen| { + do b.as_imm_buf |bp, blen| { + if (alen != blen) { false } + else { + unsafe { + libc::memcmp(ap as *libc::c_void, + bp as *libc::c_void, + alen as libc::size_t) == 0 + } + } + } + } +} + +/// Bytewise slice equality +#[cfg(test, stage0)] +#[lang="str_eq"] #[inline] pub fn eq_slice(a: &str, b: &str) -> bool { do a.as_imm_buf |ap, alen| { @@ -520,6 +634,24 @@ pub fn eq_slice(a: &str, b: &str) -> bool { } } +/// Bytewise slice equality +#[cfg(test, not(stage0))] +#[inline] +pub fn eq_slice(a: &str, b: &str) -> bool { + do a.as_imm_buf |ap, alen| { + do b.as_imm_buf |bp, blen| { + if (alen != blen) { false } + else { + unsafe { + libc::memcmp(ap as *libc::c_void, + bp as *libc::c_void, + alen as libc::size_t) == 0 + } + } + } + } +} + /// Bytewise string equality #[cfg(not(test))] #[lang="uniq_str_eq"] @@ -761,9 +893,12 @@ pub mod raw { use str::is_utf8; use vec; use vec::MutableVector; - use unstable::raw::{Slice, String}; + use unstable::raw::Slice; + #[cfg(stage0)] + use unstable::raw::String; /// Create a Rust string from a *u8 buffer of the given length + #[cfg(stage0)] pub unsafe fn from_buf_len(buf: *u8, len: uint) -> ~str { let mut v: ~[u8] = vec::with_capacity(len + 1); v.as_mut_buf(|vbuf, _len| { @@ -776,6 +911,19 @@ pub mod raw { cast::transmute(v) } + /// Create a Rust string from a *u8 buffer of the given length + #[cfg(not(stage0))] + pub unsafe fn from_buf_len(buf: *u8, len: uint) -> ~str { + let mut v: ~[u8] = vec::with_capacity(len); + do v.as_mut_buf |vbuf, _len| { + ptr::copy_memory(vbuf, buf as *u8, len) + }; + vec::raw::set_len(&mut v, len); + + assert!(is_utf8(v)); + ::cast::transmute(v) + } + /// Create a Rust string from a null-terminated C string pub unsafe fn from_c_str(buf: *libc::c_char) -> ~str { let mut curr = buf; @@ -796,17 +944,27 @@ pub mod raw { /// Converts an owned vector of bytes to a new owned string. This assumes /// that the utf-8-ness of the vector has already been validated + #[cfg(stage0)] pub unsafe fn from_bytes_owned(mut v: ~[u8]) -> ~str { v.push(0u8); cast::transmute(v) } + /// Converts an owned vector of bytes to a new owned string. This assumes + /// that the utf-8-ness of the vector has already been validated + #[cfg(not(stage0))] + #[inline] + pub unsafe fn from_bytes_owned(v: ~[u8]) -> ~str { + cast::transmute(v) + } + /// Converts a byte to a string. pub unsafe fn from_byte(u: u8) -> ~str { from_bytes([u]) } /// Form a slice from a C string. Unsafe because the caller must ensure the /// C string has the static lifetime, or else the return value may be /// invalidated later. + #[cfg(stage0)] pub unsafe fn c_str_to_static_slice(s: *libc::c_char) -> &'static str { let s = s as *u8; let mut curr = s; @@ -820,6 +978,23 @@ pub mod raw { cast::transmute(v) } + /// Form a slice from a C string. Unsafe because the caller must ensure the + /// C string has the static lifetime, or else the return value may be + /// invalidated later. + #[cfg(not(stage0))] + pub unsafe fn c_str_to_static_slice(s: *libc::c_char) -> &'static str { + let s = s as *u8; + let mut curr = s; + let mut len = 0u; + while *curr != 0u8 { + len += 1u; + curr = ptr::offset(s, len as int); + } + let v = Slice { data: s, len: len }; + assert!(is_utf8(::cast::transmute(v))); + ::cast::transmute(v) + } + /// Takes a bytewise (not UTF-8) slice from a string. /// /// Returns the substring from [`begin`..`end`). @@ -828,6 +1003,7 @@ pub mod raw { /// /// If begin is greater than end. /// If end is greater than the length of the string. + #[cfg(stage0)] #[inline] pub unsafe fn slice_bytes(s: &str, begin: uint, end: uint) -> &str { do s.as_imm_buf |sbuf, n| { @@ -841,6 +1017,28 @@ pub mod raw { } } + /// Takes a bytewise (not UTF-8) slice from a string. + /// + /// Returns the substring from [`begin`..`end`). + /// + /// # Failure + /// + /// If begin is greater than end. + /// If end is greater than the length of the string. + #[cfg(not(stage0))] + #[inline] + pub unsafe fn slice_bytes(s: &str, begin: uint, end: uint) -> &str { + do s.as_imm_buf |sbuf, n| { + assert!((begin <= end)); + assert!((end <= n)); + + cast::transmute(Slice { + data: ptr::offset(sbuf, begin as int), + len: end - begin, + }) + } + } + /// Appends a byte to a string. (Not UTF-8 safe). pub unsafe fn push_byte(s: &mut ~str, b: u8) { let new_len = s.len() + 1; @@ -877,6 +1075,7 @@ pub mod raw { } /// Sets the length of the string and adds the null terminator + #[cfg(stage0)] #[inline] pub unsafe fn set_len(v: &mut ~str, new_len: uint) { let v: **mut String = cast::transmute(v); @@ -886,6 +1085,23 @@ pub mod raw { *null = 0u8; } + /// Sets the length of a string + /// + /// This will explicitly set the size of the string, without actually + /// modifing its buffers, so it is up to the caller to ensure that + /// the string is actually the specified size. + #[cfg(not(stage0))] + #[inline] + pub unsafe fn set_len(s: &mut ~str, new_len: uint) { + let v: &mut ~[u8] = cast::transmute(s); + vec::raw::set_len(v, new_len) + } + + /// Sets the length of a string + /// + /// This will explicitly set the size of the string, without actually + /// modifing its buffers, so it is up to the caller to ensure that + /// the string is actually the specified size. #[test] fn test_from_buf_len() { unsafe { @@ -1081,10 +1297,17 @@ impl<'self> Str for @str { } impl<'self> Container for &'self str { + #[cfg(stage0)] #[inline] fn len(&self) -> uint { do self.as_imm_buf |_p, n| { n - 1u } } + + #[cfg(not(stage0))] + #[inline] + fn len(&self) -> uint { + do self.as_imm_buf |_p, n| { n } + } } impl Container for ~str { @@ -1558,6 +1781,7 @@ impl<'self> StrSlice<'self> for &'self str { } /// Copy a slice into a new unique str + #[cfg(stage0)] #[inline] fn to_owned(&self) -> ~str { do self.as_imm_buf |src, len| { @@ -1575,6 +1799,24 @@ impl<'self> StrSlice<'self> for &'self str { } } + /// Copy a slice into a new unique str + #[cfg(not(stage0))] + #[inline] + fn to_owned(&self) -> ~str { + do self.as_imm_buf |src, len| { + unsafe { + let mut v = vec::with_capacity(len); + + do v.as_mut_buf |dst, _| { + ptr::copy_memory(dst, src, len); + } + vec::raw::set_len(&mut v, len); + ::cast::transmute(v) + } + } + } + + #[cfg(stage0)] #[inline] fn to_managed(&self) -> @str { let v = at_vec::from_fn(self.len() + 1, |i| { @@ -1583,6 +1825,15 @@ impl<'self> StrSlice<'self> for &'self str { unsafe { cast::transmute(v) } } + #[cfg(not(stage0))] + #[inline] + fn to_managed(&self) -> @str { + unsafe { + let v: *&[u8] = cast::transmute(self); + cast::transmute(at_vec::to_managed(*v)) + } + } + /// Converts to a vector of `u16` encoded as UTF-16. fn to_utf16(&self) -> ~[u16] { let mut u = ~[]; @@ -1723,6 +1974,7 @@ impl<'self> StrSlice<'self> for &'self str { /// Work with the byte buffer of a string as a byte slice. /// /// The byte slice does not include the null terminator. + #[cfg(stage0)] fn as_bytes(&self) -> &'self [u8] { unsafe { let mut slice = self.repr(); @@ -1731,6 +1983,14 @@ impl<'self> StrSlice<'self> for &'self str { } } + /// Work with the byte buffer of a string as a byte slice. + /// + /// The byte slice does not include the null terminator. + #[cfg(not(stage0))] + fn as_bytes(&self) -> &'self [u8] { + unsafe { cast::transmute(*self) } + } + /// Returns the byte index of the first character of `self` that matches `search` /// /// # Return value @@ -1797,6 +2057,7 @@ impl<'self> StrSlice<'self> for &'self str { } /// Given a string, make a new string with repeated copies of it. + #[cfg(stage0)] fn repeat(&self, nn: uint) -> ~str { do self.as_imm_buf |buf, len| { // ignore the NULL terminator @@ -1818,6 +2079,27 @@ impl<'self> StrSlice<'self> for &'self str { } } + /// Given a string, make a new string with repeated copies of it. + #[cfg(not(stage0))] + fn repeat(&self, nn: uint) -> ~str { + do self.as_imm_buf |buf, len| { + let mut ret = with_capacity(nn * len); + + unsafe { + do ret.as_mut_buf |rbuf, _len| { + let mut rbuf = rbuf; + + do nn.times { + ptr::copy_memory(rbuf, buf, len); + rbuf = rbuf.offset(len as int); + } + } + raw::set_len(&mut ret, nn * len); + } + ret + } + } + /// Retrieves the first character from a string slice and returns /// it. This does not allocate a new string; instead, it returns a /// slice that point one character beyond the character that was @@ -1934,6 +2216,7 @@ pub trait OwnedStr { fn reserve(&mut self, n: uint); fn reserve_at_least(&mut self, n: uint); fn capacity(&self) -> uint; + #[cfg(stage0)] fn to_bytes_with_null(self) -> ~[u8]; /// Work with the mutable byte buffer and length of a slice. @@ -2080,6 +2363,7 @@ impl OwnedStr for ~str { /// /// * s - A string /// * n - The number of bytes to reserve space for + #[cfg(stage0)] #[inline] pub fn reserve(&mut self, n: uint) { unsafe { @@ -2088,6 +2372,29 @@ impl OwnedStr for ~str { } } + /// Reserves capacity for exactly `n` bytes in the given string, not including + /// the null terminator. + /// + /// Assuming single-byte characters, the resulting string will be large + /// enough to hold a string of length `n`. To account for the null terminator, + /// the underlying buffer will have the size `n` + 1. + /// + /// If the capacity for `s` is already equal to or greater than the requested + /// capacity, then no action is taken. + /// + /// # Arguments + /// + /// * s - A string + /// * n - The number of bytes to reserve space for + #[cfg(not(stage0))] + #[inline] + pub fn reserve(&mut self, n: uint) { + unsafe { + let v: &mut ~[u8] = cast::transmute(self); + (*v).reserve(n); + } + } + /// Reserves capacity for at least `n` bytes in the given string, not including /// the null terminator. /// @@ -2106,13 +2413,38 @@ impl OwnedStr for ~str { /// /// * s - A string /// * n - The number of bytes to reserve space for + #[cfg(stage0)] #[inline] fn reserve_at_least(&mut self, n: uint) { self.reserve(uint::next_power_of_two(n + 1u) - 1u) } + /// Reserves capacity for at least `n` bytes in the given string. + /// + /// Assuming single-byte characters, the resulting string will be large + /// enough to hold a string of length `n`. To account for the null terminator, + /// the underlying buffer will have the size `n` + 1. + /// + /// This function will over-allocate in order to amortize the allocation costs + /// in scenarios where the caller may need to repeatedly reserve additional + /// space. + /// + /// If the capacity for `s` is already equal to or greater than the requested + /// capacity, then no action is taken. + /// + /// # Arguments + /// + /// * s - A string + /// * n - The number of bytes to reserve space for + #[cfg(not(stage0))] + #[inline] + fn reserve_at_least(&mut self, n: uint) { + self.reserve(uint::next_power_of_two(n)) + } + /// Returns the number of single-byte characters the string can hold without /// reallocating + #[cfg(stage0)] fn capacity(&self) -> uint { let buf: &~[u8] = unsafe { cast::transmute(self) }; let vcap = buf.capacity(); @@ -2120,8 +2452,19 @@ impl OwnedStr for ~str { vcap - 1u } + /// Returns the number of single-byte characters the string can hold without + /// reallocating + #[cfg(not(stage0))] + fn capacity(&self) -> uint { + unsafe { + let buf: &~[u8] = cast::transmute(self); + buf.capacity() + } + } + /// Convert to a vector of bytes. This does not allocate a new /// string, and includes the null terminator. + #[cfg(stage0)] #[inline] fn to_bytes_with_null(self) -> ~[u8] { unsafe { cast::transmute(self) } @@ -2817,6 +3160,31 @@ mod tests { assert_eq!("ศไทย中华Việt Nam".as_bytes(), v); } + #[cfg(stage0)] + #[test] + #[ignore(cfg(windows))] + #[should_fail] + fn test_as_bytes_fail() { + // Don't double free. (I'm not sure if this exercises the + // original problem code path anymore.) + let s = ~""; + let _bytes = s.as_bytes(); + fail!(); + } + + #[cfg(stage0)] + #[test] + #[ignore(cfg(windows))] + #[should_fail] + fn test_as_bytes_fail() { + // Don't double free. (I'm not sure if this exercises the + // original problem code path anymore.) + let s = ~""; + let _bytes = s.as_bytes_with_null(); + fail!(); + } + + #[cfg(stage0)] #[test] fn test_to_bytes_with_null() { let s = ~"ศไทย中华Việt Nam"; @@ -2844,22 +3212,18 @@ mod tests { #[test] fn test_as_imm_buf() { - do "".as_imm_buf |buf, len| { - assert_eq!(len, 1); - unsafe { - assert_eq!(*ptr::offset(buf, 0), 0); - } + do "".as_imm_buf |_, len| { + assert_eq!(len, 0); } do "hello".as_imm_buf |buf, len| { - assert_eq!(len, 6); + assert_eq!(len, 5); unsafe { assert_eq!(*ptr::offset(buf, 0), 'h' as u8); assert_eq!(*ptr::offset(buf, 1), 'e' as u8); assert_eq!(*ptr::offset(buf, 2), 'l' as u8); assert_eq!(*ptr::offset(buf, 3), 'l' as u8); assert_eq!(*ptr::offset(buf, 4), 'o' as u8); - assert_eq!(*ptr::offset(buf, 5), 0); } } } diff --git a/src/libstd/str/ascii.rs b/src/libstd/str/ascii.rs index 3f24f98bd3d2a..f1ffa1b3bce16 100644 --- a/src/libstd/str/ascii.rs +++ b/src/libstd/str/ascii.rs @@ -15,7 +15,9 @@ use str; use str::StrSlice; use cast; use iterator::{Iterator, IteratorUtil}; -use vec::{CopyableVector, ImmutableVector, OwnedVector}; +use vec::{CopyableVector, ImmutableVector}; +#[cfg(stage0)] +use vec::OwnedVector; use to_bytes::IterBytes; use option::{Some, None}; @@ -101,19 +103,26 @@ impl<'self> AsciiCast<&'self[Ascii]> for &'self [u8] { } } -impl<'self> AsciiCast<&'self[Ascii]> for &'self str { +impl<'self> AsciiCast<&'self [Ascii]> for &'self str { #[inline] - fn to_ascii(&self) -> &'self[Ascii] { + fn to_ascii(&self) -> &'self [Ascii] { assert!(self.is_ascii()); - unsafe {self.to_ascii_nocheck()} + unsafe { self.to_ascii_nocheck() } } + #[cfg(stage0)] #[inline] - unsafe fn to_ascii_nocheck(&self) -> &'self[Ascii] { + unsafe fn to_ascii_nocheck(&self) -> &'self [Ascii] { let (p,len): (*u8, uint) = cast::transmute(*self); cast::transmute((p, len - 1)) } + #[cfg(not(stage0))] + #[inline] + unsafe fn to_ascii_nocheck(&self) -> &'self [Ascii] { + cast::transmute(*self) + } + #[inline] fn is_ascii(&self) -> bool { self.byte_iter().all(|b| b.is_ascii()) @@ -186,12 +195,19 @@ impl OwnedAsciiCast for ~str { unsafe {self.into_ascii_nocheck()} } + #[cfg(stage0)] #[inline] unsafe fn into_ascii_nocheck(self) -> ~[Ascii] { let mut r: ~[Ascii] = cast::transmute(self); r.pop(); r } + + #[cfg(not(stage0))] + #[inline] + unsafe fn into_ascii_nocheck(self) -> ~[Ascii] { + cast::transmute(self) + } } /// Trait for converting an ascii type to a string. Needed to convert `&[Ascii]` to `~str` @@ -210,11 +226,19 @@ pub trait AsciiStr { } impl<'self> AsciiStr for &'self [Ascii] { + #[cfg(stage0)] #[inline] fn to_str_ascii(&self) -> ~str { let mut cpy = self.to_owned(); cpy.push(0u8.to_ascii()); - unsafe {cast::transmute(cpy)} + unsafe { cast::transmute(cpy) } + } + + #[cfg(not(stage0))] + #[inline] + fn to_str_ascii(&self) -> ~str { + let cpy = self.to_owned(); + unsafe { cast::transmute(cpy) } } #[inline] @@ -234,11 +258,18 @@ impl<'self> AsciiStr for &'self [Ascii] { } impl ToStrConsume for ~[Ascii] { + #[cfg(stage0)] #[inline] fn into_str(self) -> ~str { let mut cpy = self; cpy.push(0u8.to_ascii()); - unsafe {cast::transmute(cpy)} + unsafe { cast::transmute(cpy) } + } + + #[cfg(not(stage0))] + #[inline] + fn into_str(self) -> ~str { + unsafe { cast::transmute(self) } } } @@ -257,7 +288,7 @@ pub trait ToBytesConsume { impl ToBytesConsume for ~[Ascii] { fn into_bytes(self) -> ~[u8] { - unsafe {cast::transmute(self)} + unsafe { cast::transmute(self) } } } diff --git a/src/libstd/unstable/extfmt.rs b/src/libstd/unstable/extfmt.rs index 5417af5008131..a08851df626c6 100644 --- a/src/libstd/unstable/extfmt.rs +++ b/src/libstd/unstable/extfmt.rs @@ -549,12 +549,14 @@ pub mod rt { // For strings, precision is the maximum characters // displayed let unpadded = match cv.precision { - CountImplied => s, - CountIs(max) => if (max as uint) < s.char_len() { - s.slice(0, max as uint) - } else { - s - } + CountImplied => s, + CountIs(max) => { + if (max as uint) < s.char_len() { + s.slice(0, max as uint) + } else { + s + } + } }; pad(cv, unpadded, None, PadNozero, buf); } diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index d77a9f58a3853..dffa7232e3487 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -302,10 +302,9 @@ void tm_to_rust_tm(tm* in_tm, rust_tm* out_tm, int32_t gmtoff, if (zone != NULL) { size_t size = strlen(zone); - reserve_vec_exact(&out_tm->tm_zone, size + 1); + reserve_vec_exact(&out_tm->tm_zone, size); memcpy(out_tm->tm_zone->data, zone, size); - out_tm->tm_zone->fill = size + 1; - out_tm->tm_zone->data[size] = '\0'; + out_tm->tm_zone->fill = size; } } From 986ba9c3c17ee1c7bb05ba9651fd0a6547cc2db6 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 4 Aug 2013 18:37:55 -0700 Subject: [PATCH 14/26] std: Update the c_str docs, and support CString not owning the pointer --- src/libstd/c_str.rs | 201 ++++++++++++++------------------------------ 1 file changed, 63 insertions(+), 138 deletions(-) diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 29aa68b1533e4..312cfe5460367 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -18,105 +18,77 @@ use ptr; use str::StrSlice; use vec::ImmutableVector; -/** - * 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. - */ +/// 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. pub struct CString { priv buf: *libc::c_char, + priv owns_buffer_: bool, } impl<'self> CString { - /** - * Create a C String from a str. - */ - pub fn from_str(s: &str) -> CString { - s.to_c_str() + /// Create a C String from a pointer. + pub fn new(buf: *libc::c_char, owns_buffer: bool) -> CString { + CString { buf: buf, owns_buffer_: owns_buffer } } - /** - * Take the wrapped `*libc::c_char` from the `CString` wrapper. - * - * # Failure - * - * If the wrapper is empty. - */ - pub unsafe fn take(&mut self) -> *libc::c_char { - if self.buf.is_null() { - fail!("CString has no wrapped `*libc::c_char`"); - } - let buf = self.buf; - self.buf = ptr::null(); - buf - } - - /** - * Puts a `*libc::c_char` into the `CString` wrapper. - * - * # Failure - * - * If the `*libc::c_char` is null. - * If the wrapper is not empty. - */ - pub fn put_back(&mut self, buf: *libc::c_char) { - if buf.is_null() { - fail!("attempt to put a null pointer into a CString"); - } - if self.buf.is_not_null() { - fail!("CString already wraps a `*libc::c_char`"); - } - self.buf = buf; + /// Unwraps the wrapped `*libc::c_char` from the `CString` wrapper. + pub unsafe fn unwrap(self) -> *libc::c_char { + let mut c_str = self; + c_str.owns_buffer_ = false; + c_str.buf } - /** - * Calls a closure with a reference to the underlying `*libc::c_char`. - */ + /// Calls a closure with a reference to the underlying `*libc::c_char`. + /// + /// # Failure + /// + /// Fails if the CString is null. pub fn with_ref(&self, f: &fn(*libc::c_char) -> T) -> T { - if self.buf.is_null() { - fail!("CString already wraps a `*libc::c_char`"); - } + if self.buf.is_null() { fail!("CString is null!"); } f(self.buf) } - /** - * Calls a closure with a mutable reference to the underlying `*libc::c_char`. - */ + /// Calls a closure with a mutable reference to the underlying `*libc::c_char`. + /// + /// # Failure + /// + /// Fails if the CString is null. pub fn with_mut_ref(&mut self, f: &fn(*mut libc::c_char) -> T) -> T { - if self.buf.is_not_null() { - fail!("CString already wraps a `*libc::c_char`"); - } + if self.buf.is_null() { fail!("CString is null!"); } f(unsafe { cast::transmute(self.buf) }) } - /** - * Returns true if the CString does not wrap a `*libc::c_char`. - */ - pub fn is_empty(&self) -> bool { + /// Returns true if the CString is a null. + pub fn is_null(&self) -> bool { self.buf.is_null() } - /** - * Returns true if the CString wraps a `*libc::c_char`. - */ - pub fn is_not_empty(&self) -> bool { + /// Returns true if the CString is not null. + pub fn is_not_null(&self) -> bool { self.buf.is_not_null() } - /** - * Converts the CString into a `&[u8]` without copying. - */ + /// 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. + /// + /// # Failure + /// + /// Fails if the CString is null. pub fn as_bytes(&self) -> &'self [u8] { + if self.buf.is_null() { fail!("CString is null!"); } unsafe { let len = libc::strlen(self.buf) as uint; cast::transmute((self.buf, len + 1)) } } - /** - * Return a CString iterator. - */ + /// Return a CString iterator. fn iter(&self) -> CStringIterator<'self> { CStringIterator { ptr: self.buf, @@ -127,37 +99,28 @@ impl<'self> CString { impl Drop for CString { fn drop(&self) { - if self.buf.is_not_null() { + if self.owns_buffer_ && self.buf.is_not_null() { unsafe { libc::free(self.buf as *libc::c_void) - }; + } } } } -/** - * A generic trait for converting a value to a CString. - */ +/// A generic trait for converting a value to a CString. pub trait ToCStr { - /** - * Create a C String. - */ + /// Create a C String. fn to_c_str(&self) -> CString; } impl<'self> ToCStr for &'self str { - /** - * Create a C String from a `&str`. - */ + #[inline] fn to_c_str(&self) -> CString { self.as_bytes().to_c_str() } } impl<'self> ToCStr for &'self [u8] { - /** - * Create a C String from a `&[u8]`. - */ fn to_c_str(&self) -> CString { do self.as_imm_buf |self_buf, self_len| { unsafe { @@ -168,26 +131,22 @@ impl<'self> ToCStr for &'self [u8] { ptr::copy_memory(buf, self_buf, self_len); *ptr::mut_offset(buf, self_len as int) = 0; - CString { buf: buf as *libc::c_char } + + CString::new(buf as *libc::c_char, true) } } } } -/** - * External iterator for a CString's bytes. - * - * Use with the `std::iterator` module. - */ +/// External iterator for a CString's bytes. +/// +/// Use with the `std::iterator` module. pub struct CStringIterator<'self> { priv ptr: *libc::c_char, priv lifetime: &'self libc::c_char, // FIXME: #5922 } impl<'self> Iterator for CStringIterator<'self> { - /** - * Advance the iterator. - */ fn next(&mut self) -> Option { if self.ptr.is_null() { None @@ -226,66 +185,32 @@ mod tests { } #[test] - fn test_take() { - let mut c_str = "hello".to_c_str(); - unsafe { libc::free(c_str.take() as *libc::c_void) } - assert!(c_str.is_empty()); - } - - #[test] - fn test_take_and_put_back() { - let mut c_str = "hello".to_c_str(); - assert!(c_str.is_not_empty()); - - let buf = unsafe { c_str.take() }; - - assert!(c_str.is_empty()); - - c_str.put_back(buf); - - assert!(c_str.is_not_empty()); - } - - #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_take_empty_fail() { - let mut c_str = "hello".to_c_str(); - unsafe { - libc::free(c_str.take() as *libc::c_void); - c_str.take(); - } + fn test_is_null() { + let c_str = CString::new(ptr::null(), false); + assert!(c_str.is_null()); + assert!(!c_str.is_not_null()); } #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_put_back_null_fail() { - let mut c_str = "hello".to_c_str(); - c_str.put_back(ptr::null()); + fn test_unwrap() { + let c_str = "hello".to_c_str(); + unsafe { libc::free(c_str.unwrap() as *libc::c_void) } } #[test] - #[should_fail] - #[ignore(cfg(windows))] - fn test_put_back_full_fail() { - let mut c_str = "hello".to_c_str(); - c_str.put_back(0xdeadbeef as *libc::c_char); - } - - fn test_with() { + fn test_with_ref() { let c_str = "hello".to_c_str(); let len = unsafe { c_str.with_ref(|buf| libc::strlen(buf)) }; - assert!(c_str.is_not_empty()); + assert!(!c_str.is_null()); + assert!(c_str.is_not_null()); assert_eq!(len, 5); } #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_with_empty_fail() { - let mut c_str = "hello".to_c_str(); - unsafe { libc::free(c_str.take() as *libc::c_void) } + fn test_with_ref_empty_fail() { + let c_str = CString::new(ptr::null(), false); c_str.with_ref(|_| ()); } } From c8e454097bf44b1b5b91b46da08dabe9f140a6ee Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 5 Aug 2013 19:54:49 -0700 Subject: [PATCH 15/26] std: fix a typo where .to_c_str wasn't being called on android --- src/libstd/os.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 6d109aac4ea6c..0e25d3dbe040c 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -467,7 +467,7 @@ pub fn self_exe_path() -> Option { let mut path = [0 as c_char, .. TMPBUF_SZ]; do path.as_mut_buf |buf, len| { - let len = do "/proc/self/exe".to_c_str.with_ref |proc_self_buf| { + let len = do "/proc/self/exe".to_c_str().with_ref |proc_self_buf| { readlink(proc_self_buf, buf, len as size_t) as uint }; From fb9b27910b41e714dfd6b3ccc48161260943c9cf Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 5 Aug 2013 19:55:07 -0700 Subject: [PATCH 16/26] std: c_str should use regions on methods --- src/libstd/c_str.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 312cfe5460367..6c5853019b03b 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -27,7 +27,7 @@ pub struct CString { priv owns_buffer_: bool, } -impl<'self> CString { +impl CString { /// Create a C String from a pointer. pub fn new(buf: *libc::c_char, owns_buffer: bool) -> CString { CString { buf: buf, owns_buffer_: owns_buffer } @@ -80,7 +80,7 @@ impl<'self> CString { /// # Failure /// /// Fails if the CString is null. - pub fn as_bytes(&self) -> &'self [u8] { + pub fn as_bytes<'a>(&'a self) -> &'a [u8] { if self.buf.is_null() { fail!("CString is null!"); } unsafe { let len = libc::strlen(self.buf) as uint; @@ -89,7 +89,7 @@ impl<'self> CString { } /// Return a CString iterator. - fn iter(&self) -> CStringIterator<'self> { + fn iter<'a>(&'a self) -> CStringIterator<'a> { CStringIterator { ptr: self.buf, lifetime: unsafe { cast::transmute(self.buf) }, From 5e7b6662502a82b6f1a789277d3dc2e68aa4eb13 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Tue, 6 Aug 2013 16:19:31 -0700 Subject: [PATCH 17/26] std: update str.push_byte to work without str trailing nulls --- src/libstd/str.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libstd/str.rs b/src/libstd/str.rs index a77371bbfc925..60148e1432c01 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -1064,15 +1064,24 @@ pub mod raw { } /// Appends a byte to a string. (Not UTF-8 safe). + #[cfg(stage0)] pub unsafe fn push_byte(s: &mut ~str, b: u8) { let new_len = s.len() + 1; s.reserve_at_least(new_len); do s.as_mut_buf |buf, len| { - *ptr::mut_offset(buf, (len-1) as int) = b; + *ptr::mut_offset(buf, len as int) = b; } set_len(&mut *s, new_len); } + /// Appends a byte to a string. (Not UTF-8 safe). + #[cfg(not(stage0))] + #[inline] + pub unsafe fn push_byte(s: &mut ~str, b: u8) { + let v: &mut ~[u8] = cast::transmute(s); + v.push(b); + } + /// Appends a vector of bytes to a string. (Not UTF-8 safe). unsafe fn push_bytes(s: &mut ~str, bytes: &[u8]) { let new_len = s.len() + bytes.len(); From e053bff5d0c83a5b36afcaf4c58a40a27162ec0f Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Tue, 6 Aug 2013 21:06:12 -0700 Subject: [PATCH 18/26] std: Fix c_str.iter() and add test --- src/libstd/c_str.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 6c5853019b03b..25c6d4379fa2c 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -148,10 +148,10 @@ pub struct CStringIterator<'self> { impl<'self> Iterator for CStringIterator<'self> { fn next(&mut self) -> Option { - if self.ptr.is_null() { + let ch = unsafe { *self.ptr }; + if ch == 0 { None } else { - let ch = unsafe { *self.ptr }; self.ptr = ptr::offset(self.ptr, 1); Some(ch) } @@ -163,6 +163,7 @@ mod tests { use super::*; use libc; use ptr; + use option::{Some, None}; #[test] fn test_to_c_str() { @@ -210,7 +211,23 @@ mod tests { #[should_fail] #[ignore(cfg(windows))] fn test_with_ref_empty_fail() { - let c_str = CString::new(ptr::null(), false); + let c_str = unsafe { CString::new(ptr::null(), false) }; c_str.with_ref(|_| ()); } + + #[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); + } } From d6257b315be2a18ccb3a28c48a73b1619ab57df4 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Tue, 6 Aug 2013 21:08:39 -0700 Subject: [PATCH 19/26] std: remove unnecessary test from c_str.drop and use safer transmute --- src/libstd/c_str.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 25c6d4379fa2c..fef16581c020a 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -57,7 +57,7 @@ impl CString { /// Fails if the CString is null. pub fn with_mut_ref(&mut self, f: &fn(*mut libc::c_char) -> T) -> T { if self.buf.is_null() { fail!("CString is null!"); } - f(unsafe { cast::transmute(self.buf) }) + f(unsafe { cast::transmute_mut_unsafe(self.buf) }) } /// Returns true if the CString is a null. @@ -99,7 +99,7 @@ impl CString { impl Drop for CString { fn drop(&self) { - if self.owns_buffer_ && self.buf.is_not_null() { + if self.owns_buffer_ { unsafe { libc::free(self.buf as *libc::c_void) } From 72688eaa132403fcb5f103cb13756ef50ed804c0 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Tue, 6 Aug 2013 21:11:10 -0700 Subject: [PATCH 20/26] std: Make CString::new unsafe b/c it can mutate a *T ptr --- src/libstd/c_str.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index fef16581c020a..ac9bbfb1dfb63 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -29,7 +29,7 @@ pub struct CString { impl CString { /// Create a C String from a pointer. - pub fn new(buf: *libc::c_char, owns_buffer: bool) -> CString { + pub unsafe fn new(buf: *libc::c_char, owns_buffer: bool) -> CString { CString { buf: buf, owns_buffer_: owns_buffer } } @@ -187,7 +187,7 @@ mod tests { #[test] fn test_is_null() { - let c_str = CString::new(ptr::null(), false); + let c_str = unsafe { CString::new(ptr::null(), false) }; assert!(c_str.is_null()); assert!(!c_str.is_not_null()); } From aababbba8e5d5b56aa4a0cedb9b2008ba6cdc422 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Tue, 6 Aug 2013 21:19:38 -0700 Subject: [PATCH 21/26] std: fix a bad type cast for in str.to_c_str() --- src/libstd/c_str.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index ac9bbfb1dfb63..7e31354366084 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -124,7 +124,7 @@ impl<'self> ToCStr for &'self [u8] { fn to_c_str(&self) -> CString { do self.as_imm_buf |self_buf, self_len| { unsafe { - let buf = libc::malloc(self_len as u64 + 1) as *mut u8; + let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8; if buf.is_null() { fail!("failed to allocate memory!"); } From e7f08822147c495592bef83eeb835640a6342a2a Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Wed, 7 Aug 2013 20:25:38 -0700 Subject: [PATCH 22/26] Fix a stack to use the new .to_c_str() api --- src/test/run-pass/c-stack-returning-int64.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/run-pass/c-stack-returning-int64.rs b/src/test/run-pass/c-stack-returning-int64.rs index 9775f1ef45a74..e91c11f5cd0a7 100644 --- a/src/test/run-pass/c-stack-returning-int64.rs +++ b/src/test/run-pass/c-stack-returning-int64.rs @@ -20,11 +20,11 @@ mod libc { } fn atol(s: ~str) -> int { - s.as_imm_buf(|x, _len| unsafe { libc::atol(x) }) + s.to_c_str().with_ref(|x| unsafe { libc::atol(x as *u8) }) } fn atoll(s: ~str) -> i64 { - s.as_imm_buf(|x, _len| unsafe { libc::atoll(x) }) + s.to_c_str().with_ref(|x| unsafe { libc::atoll(x as *u8) }) } pub fn main() { From 7a5ee374e249177c02e00ad117d2745d6edf2e0b Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Wed, 7 Aug 2013 21:07:06 -0700 Subject: [PATCH 23/26] std: import HANDLE for os::list_dir for windows --- src/libstd/os.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 1f63a7c604147..d673470f064f3 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -732,6 +732,7 @@ pub fn list_dir(p: &Path) -> ~[~str] { FindNextFileW, FindClose, }; + use libc::types::os::arch::extra::HANDLE; use os::win32::{ as_utf16_p }; From 03cc757fe90b88895fcf911d9cce5c04a008b127 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 8 Aug 2013 06:37:55 -0700 Subject: [PATCH 24/26] std: more fixes for os.rs for windows --- src/libstd/os.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index d673470f064f3..26f6dbc2b37cc 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -642,7 +642,8 @@ pub fn make_dir(p: &Path, mode: c_int) -> bool { use os::win32::as_utf16_p; // FIXME: turn mode into something useful? #2623 do as_utf16_p(p.to_str()) |buf| { - libc::CreateDirectoryW(buf, ptr::null()) != (0 as libc::BOOL) + libc::CreateDirectoryW(buf, ptr::null() as LPCWSTR) + != (0 as libc::BOOL) } } } @@ -1081,7 +1082,7 @@ pub fn last_os_error() -> ~str { let mut buf = [0 as c_char, ..TMPBUF_SZ]; - do buf.as_imm_buf |buf, len| { + do buf.as_mut_buf |buf, len| { unsafe { let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, From c14e14e63aeefa5c94648dd921bd99a9dadeb060 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 9 Aug 2013 11:13:26 -0700 Subject: [PATCH 25/26] std: more windows fixes to os.rs and run.rs --- src/libstd/os.rs | 8 +++++--- src/libstd/run.rs | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 57415e89cfb98..365ddeb4f64d8 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -640,7 +640,7 @@ pub fn make_dir(p: &Path, mode: c_int) -> bool { use os::win32::as_utf16_p; // FIXME: turn mode into something useful? #2623 do as_utf16_p(p.to_str()) |buf| { - libc::CreateDirectoryW(buf, ptr::null() as LPCWSTR) + libc::CreateDirectoryW(buf, ptr::mut_null()) != (0 as libc::BOOL) } } @@ -1080,8 +1080,8 @@ pub fn last_os_error() -> ~str { let mut buf = [0 as c_char, ..TMPBUF_SZ]; - do buf.as_mut_buf |buf, len| { - unsafe { + unsafe { + do buf.as_mut_buf |buf, len| { let res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, ptr::mut_null(), @@ -1093,7 +1093,9 @@ pub fn last_os_error() -> ~str { if res == 0 { fail!("[%?] FormatMessage failure", errno()); } + } + do buf.as_imm_buf |buf, _len| { str::raw::from_c_str(buf) } } diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 83e0846c35118..23ee52389a093 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -25,7 +25,6 @@ use prelude::*; use ptr; use task; use vec::ImmutableVector; -use vec; /** * A value representing a child process. @@ -691,6 +690,8 @@ fn spawn_process_os(prog: &str, args: &[~str], #[cfg(unix)] fn with_argv(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T { + use vec; + // We can't directly convert `str`s into `*char`s, as someone needs to hold // a reference to the intermediary byte buffers. So first build an array to // hold all the ~[u8] byte strings. @@ -717,6 +718,8 @@ fn with_argv(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T { #[cfg(unix)] fn with_envp(env: Option<&[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T { + use vec; + // On posixy systems we can pass a char** for envp, which is a // null-terminated array of "k=v\n" strings. Like `with_argv`, we have to // have a temporary buffer to hold the intermediary `~[u8]` byte strings. From cab6d46e58ea6f7535d8e454f0345eccfae183c4 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 9 Aug 2013 14:06:03 -0700 Subject: [PATCH 26/26] rustpkg: another fix for windows --- src/librustpkg/util.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index a7e92afd2a4e4..3a0954c62d7d7 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -9,7 +9,6 @@ // except according to those terms. use std::{os, result}; -use std::c_str::ToCStr; use rustc::driver::{driver, session}; use rustc::metadata::filesearch; use extra::getopts::groups::getopts; @@ -373,7 +372,9 @@ pub fn link_exe(_src: &Path, _dest: &Path) -> bool { #[cfg(target_os = "freebsd")] #[cfg(target_os = "macos")] pub fn link_exe(src: &Path, dest: &Path) -> bool { + use std::c_str::ToCStr; use std::libc; + unsafe { do src.to_c_str().with_ref |src_buf| { do dest.to_c_str().with_ref |dest_buf| {