|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 | 11 | use cast;
|
12 |
| -use iterator::Iterator; |
| 12 | +use iterator::{Iterator,range}; |
13 | 13 | use libc;
|
14 | 14 | use ops::Drop;
|
15 | 15 | use option::{Option, Some, None};
|
16 | 16 | use ptr::RawPtr;
|
17 | 17 | use ptr;
|
18 | 18 | use str::StrSlice;
|
19 |
| -use vec::ImmutableVector; |
| 19 | +use vec::{ImmutableVector,CopyableVector}; |
| 20 | +use container::Container; |
| 21 | + |
| 22 | +/// Resolution options for the `null_byte` condition |
| 23 | +pub enum NullByteResolution { |
| 24 | + /// Truncate at the null byte |
| 25 | + Truncate, |
| 26 | + /// Use a replacement byte |
| 27 | + ReplaceWith(libc::c_char) |
| 28 | +} |
| 29 | + |
| 30 | +condition! { |
| 31 | + // this should be &[u8] but there's a lifetime issue |
| 32 | + null_byte: (~[u8]) -> super::NullByteResolution; |
| 33 | +} |
20 | 34 |
|
21 | 35 | /// The representation of a C String.
|
22 | 36 | ///
|
@@ -110,31 +124,59 @@ impl Drop for CString {
|
110 | 124 |
|
111 | 125 | /// A generic trait for converting a value to a CString.
|
112 | 126 | pub trait ToCStr {
|
113 |
| - /// Create a C String. |
| 127 | + /// Copy the receiver into a CString. |
| 128 | + /// |
| 129 | + /// # Failure |
| 130 | + /// |
| 131 | + /// Raises the `null_byte` condition if the receiver has an interior null. |
114 | 132 | fn to_c_str(&self) -> CString;
|
| 133 | + |
| 134 | + /// Unsafe variant of `to_c_str()` that doesn't check for nulls. |
| 135 | + unsafe fn to_c_str_unchecked(&self) -> CString; |
115 | 136 | }
|
116 | 137 |
|
117 | 138 | impl<'self> ToCStr for &'self str {
|
118 | 139 | #[inline]
|
119 | 140 | fn to_c_str(&self) -> CString {
|
120 | 141 | self.as_bytes().to_c_str()
|
121 | 142 | }
|
| 143 | + |
| 144 | + #[inline] |
| 145 | + unsafe fn to_c_str_unchecked(&self) -> CString { |
| 146 | + self.as_bytes().to_c_str_unchecked() |
| 147 | + } |
122 | 148 | }
|
123 | 149 |
|
124 | 150 | impl<'self> ToCStr for &'self [u8] {
|
125 | 151 | fn to_c_str(&self) -> CString {
|
126 |
| - do self.as_imm_buf |self_buf, self_len| { |
127 |
| - unsafe { |
128 |
| - let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8; |
129 |
| - if buf.is_null() { |
130 |
| - fail!("failed to allocate memory!"); |
| 152 | + let mut cs = unsafe { self.to_c_str_unchecked() }; |
| 153 | + do cs.with_mut_ref |buf| { |
| 154 | + for i in range(0, self.len()) { |
| 155 | + unsafe { |
| 156 | + let p = buf.offset_inbounds(i as int); |
| 157 | + if *p == 0 { |
| 158 | + match null_byte::cond.raise(self.to_owned()) { |
| 159 | + Truncate => break, |
| 160 | + ReplaceWith(c) => *p = c |
| 161 | + } |
| 162 | + } |
131 | 163 | }
|
| 164 | + } |
| 165 | + } |
| 166 | + cs |
| 167 | + } |
132 | 168 |
|
133 |
| - ptr::copy_memory(buf, self_buf, self_len); |
134 |
| - *ptr::mut_offset(buf, self_len as int) = 0; |
135 |
| - |
136 |
| - CString::new(buf as *libc::c_char, true) |
| 169 | + unsafe fn to_c_str_unchecked(&self) -> CString { |
| 170 | + do self.as_imm_buf |self_buf, self_len| { |
| 171 | + let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8; |
| 172 | + if buf.is_null() { |
| 173 | + fail!("failed to allocate memory!"); |
137 | 174 | }
|
| 175 | + |
| 176 | + ptr::copy_memory(buf, self_buf, self_len); |
| 177 | + *ptr::mut_offset(buf, self_len as int) = 0; |
| 178 | + |
| 179 | + CString::new(buf as *libc::c_char, true) |
138 | 180 | }
|
139 | 181 | }
|
140 | 182 | }
|
@@ -231,4 +273,49 @@ mod tests {
|
231 | 273 | assert_eq!(iter.next(), Some('o' as libc::c_char));
|
232 | 274 | assert_eq!(iter.next(), None);
|
233 | 275 | }
|
| 276 | + |
| 277 | + #[test] |
| 278 | + #[ignore(cfg(windows))] |
| 279 | + fn test_to_c_str_fail() { |
| 280 | + use c_str::null_byte::cond; |
| 281 | + |
| 282 | + let mut error_happened = false; |
| 283 | + do cond.trap(|err| { |
| 284 | + assert_eq!(err, bytes!("he", 0, "llo").to_owned()) |
| 285 | + error_happened = true; |
| 286 | + Truncate |
| 287 | + }).inside { |
| 288 | + "he\x00llo".to_c_str() |
| 289 | + }; |
| 290 | + assert!(error_happened); |
| 291 | + |
| 292 | + do cond.trap(|_| { |
| 293 | + ReplaceWith('?' as libc::c_char) |
| 294 | + }).inside(|| "he\x00llo".to_c_str()).with_ref |buf| { |
| 295 | + unsafe { |
| 296 | + assert_eq!(*buf.offset(0), 'h' as libc::c_char); |
| 297 | + assert_eq!(*buf.offset(1), 'e' as libc::c_char); |
| 298 | + assert_eq!(*buf.offset(2), '?' as libc::c_char); |
| 299 | + assert_eq!(*buf.offset(3), 'l' as libc::c_char); |
| 300 | + assert_eq!(*buf.offset(4), 'l' as libc::c_char); |
| 301 | + assert_eq!(*buf.offset(5), 'o' as libc::c_char); |
| 302 | + assert_eq!(*buf.offset(6), 0); |
| 303 | + } |
| 304 | + } |
| 305 | + } |
| 306 | + |
| 307 | + #[test] |
| 308 | + fn test_to_c_str_unchecked() { |
| 309 | + unsafe { |
| 310 | + do "he\x00llo".to_c_str_unchecked().with_ref |buf| { |
| 311 | + assert_eq!(*buf.offset(0), 'h' as libc::c_char); |
| 312 | + assert_eq!(*buf.offset(1), 'e' as libc::c_char); |
| 313 | + assert_eq!(*buf.offset(2), 0); |
| 314 | + assert_eq!(*buf.offset(3), 'l' as libc::c_char); |
| 315 | + assert_eq!(*buf.offset(4), 'l' as libc::c_char); |
| 316 | + assert_eq!(*buf.offset(5), 'o' as libc::c_char); |
| 317 | + assert_eq!(*buf.offset(6), 0); |
| 318 | + } |
| 319 | + } |
| 320 | + } |
234 | 321 | }
|
0 commit comments