]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2012 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
62682a34 SL |
11 | use borrow::{Cow, ToOwned}; |
12 | use boxed::Box; | |
13 | use clone::Clone; | |
c34b1796 | 14 | use convert::{Into, From}; |
85aaf69f | 15 | use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; |
c34b1796 | 16 | use error::Error; |
1a4d82fc | 17 | use fmt; |
85aaf69f | 18 | use io; |
c34b1796 | 19 | use iter::Iterator; |
1a4d82fc JJ |
20 | use libc; |
21 | use mem; | |
22 | use ops::Deref; | |
85aaf69f SL |
23 | use option::Option::{self, Some, None}; |
24 | use result::Result::{self, Ok, Err}; | |
c34b1796 | 25 | use slice; |
62682a34 | 26 | use str; |
1a4d82fc JJ |
27 | use string::String; |
28 | use vec::Vec; | |
29 | ||
85aaf69f | 30 | /// A type representing an owned C-compatible string |
1a4d82fc | 31 | /// |
85aaf69f | 32 | /// This type serves the primary purpose of being able to safely generate a |
1a4d82fc JJ |
33 | /// C-compatible string from a Rust byte slice or vector. An instance of this |
34 | /// type is a static guarantee that the underlying bytes contain no interior 0 | |
35 | /// bytes and the final byte is 0. | |
36 | /// | |
37 | /// A `CString` is created from either a byte slice or a byte vector. After | |
38 | /// being created, a `CString` predominately inherits all of its methods from | |
39 | /// the `Deref` implementation to `[libc::c_char]`. Note that the underlying | |
40 | /// array is represented as an array of `libc::c_char` as opposed to `u8`. A | |
41 | /// `u8` slice can be obtained with the `as_bytes` method. Slices produced from | |
42 | /// a `CString` do *not* contain the trailing nul terminator unless otherwise | |
43 | /// specified. | |
44 | /// | |
c34b1796 | 45 | /// # Examples |
1a4d82fc JJ |
46 | /// |
47 | /// ```no_run | |
c34b1796 | 48 | /// # #![feature(libc)] |
1a4d82fc JJ |
49 | /// # extern crate libc; |
50 | /// # fn main() { | |
51 | /// use std::ffi::CString; | |
52 | /// use libc; | |
53 | /// | |
54 | /// extern { | |
55 | /// fn my_printer(s: *const libc::c_char); | |
56 | /// } | |
57 | /// | |
62682a34 | 58 | /// let c_to_print = CString::new("Hello, world!").unwrap(); |
1a4d82fc JJ |
59 | /// unsafe { |
60 | /// my_printer(c_to_print.as_ptr()); | |
61 | /// } | |
62 | /// # } | |
63 | /// ``` | |
62682a34 | 64 | #[derive(PartialEq, PartialOrd, Eq, Ord, Hash)] |
c34b1796 | 65 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc | 66 | pub struct CString { |
62682a34 | 67 | inner: Box<[u8]>, |
85aaf69f SL |
68 | } |
69 | ||
70 | /// Representation of a borrowed C string. | |
71 | /// | |
72 | /// This dynamically sized type is only safely constructed via a borrowed | |
73 | /// version of an instance of `CString`. This type can be constructed from a raw | |
74 | /// C string as well and represents a C string borrowed from another location. | |
75 | /// | |
76 | /// Note that this structure is **not** `repr(C)` and is not recommended to be | |
77 | /// placed in the signatures of FFI functions. Instead safe wrappers of FFI | |
78 | /// functions may leverage the unsafe `from_ptr` constructor to provide a safe | |
79 | /// interface to other consumers. | |
80 | /// | |
81 | /// # Examples | |
82 | /// | |
83 | /// Inspecting a foreign C string | |
84 | /// | |
85 | /// ```no_run | |
c34b1796 | 86 | /// # #![feature(libc)] |
85aaf69f SL |
87 | /// extern crate libc; |
88 | /// use std::ffi::CStr; | |
89 | /// | |
90 | /// extern { fn my_string() -> *const libc::c_char; } | |
91 | /// | |
92 | /// fn main() { | |
93 | /// unsafe { | |
94 | /// let slice = CStr::from_ptr(my_string()); | |
95 | /// println!("string length: {}", slice.to_bytes().len()); | |
96 | /// } | |
97 | /// } | |
98 | /// ``` | |
99 | /// | |
100 | /// Passing a Rust-originating C string | |
101 | /// | |
102 | /// ```no_run | |
c34b1796 | 103 | /// # #![feature(libc)] |
85aaf69f SL |
104 | /// extern crate libc; |
105 | /// use std::ffi::{CString, CStr}; | |
106 | /// | |
107 | /// fn work(data: &CStr) { | |
108 | /// extern { fn work_with(data: *const libc::c_char); } | |
109 | /// | |
110 | /// unsafe { work_with(data.as_ptr()) } | |
111 | /// } | |
112 | /// | |
113 | /// fn main() { | |
114 | /// let s = CString::new("data data data data").unwrap(); | |
115 | /// work(&s); | |
116 | /// } | |
117 | /// ``` | |
62682a34 SL |
118 | /// |
119 | /// Converting a foreign C string into a Rust `String` | |
120 | /// | |
121 | /// ```no_run | |
122 | /// # #![feature(libc,cstr_to_str)] | |
123 | /// extern crate libc; | |
124 | /// use std::ffi::CStr; | |
125 | /// | |
126 | /// extern { fn my_string() -> *const libc::c_char; } | |
127 | /// | |
128 | /// fn my_string_safe() -> String { | |
129 | /// unsafe { | |
130 | /// CStr::from_ptr(my_string()).to_string_lossy().into_owned() | |
131 | /// } | |
132 | /// } | |
133 | /// | |
134 | /// fn main() { | |
135 | /// println!("string: {}", my_string_safe()); | |
136 | /// } | |
137 | /// ``` | |
85aaf69f | 138 | #[derive(Hash)] |
c34b1796 | 139 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f | 140 | pub struct CStr { |
c34b1796 AL |
141 | // FIXME: this should not be represented with a DST slice but rather with |
142 | // just a raw `libc::c_char` along with some form of marker to make | |
143 | // this an unsized type. Essentially `sizeof(&CStr)` should be the | |
144 | // same as `sizeof(&c_char)` but `CStr` should be an unsized type. | |
85aaf69f SL |
145 | inner: [libc::c_char] |
146 | } | |
147 | ||
148 | /// An error returned from `CString::new` to indicate that a nul byte was found | |
149 | /// in the vector provided. | |
150 | #[derive(Clone, PartialEq, Debug)] | |
c34b1796 | 151 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
152 | pub struct NulError(usize, Vec<u8>); |
153 | ||
1a4d82fc | 154 | impl CString { |
9346a6ac | 155 | /// Creates a new C-compatible string from a container of bytes. |
85aaf69f SL |
156 | /// |
157 | /// This method will consume the provided data and use the underlying bytes | |
158 | /// to construct a new string, ensuring that there is a trailing 0 byte. | |
159 | /// | |
160 | /// # Examples | |
161 | /// | |
162 | /// ```no_run | |
c34b1796 | 163 | /// # #![feature(libc)] |
85aaf69f SL |
164 | /// extern crate libc; |
165 | /// use std::ffi::CString; | |
166 | /// | |
167 | /// extern { fn puts(s: *const libc::c_char); } | |
168 | /// | |
169 | /// fn main() { | |
170 | /// let to_print = CString::new("Hello!").unwrap(); | |
171 | /// unsafe { | |
172 | /// puts(to_print.as_ptr()); | |
173 | /// } | |
174 | /// } | |
175 | /// ``` | |
176 | /// | |
177 | /// # Errors | |
178 | /// | |
179 | /// This function will return an error if the bytes yielded contain an | |
180 | /// internal 0 byte. The error returned will contain the bytes as well as | |
181 | /// the position of the nul byte. | |
c34b1796 AL |
182 | #[stable(feature = "rust1", since = "1.0.0")] |
183 | pub fn new<T: Into<Vec<u8>>>(t: T) -> Result<CString, NulError> { | |
184 | let bytes = t.into(); | |
85aaf69f SL |
185 | match bytes.iter().position(|x| *x == 0) { |
186 | Some(i) => Err(NulError(i, bytes)), | |
187 | None => Ok(unsafe { CString::from_vec_unchecked(bytes) }), | |
188 | } | |
189 | } | |
190 | ||
9346a6ac | 191 | /// Creates a C-compatible string from a byte vector without checking for |
1a4d82fc JJ |
192 | /// interior 0 bytes. |
193 | /// | |
9346a6ac AL |
194 | /// This method is equivalent to `new` except that no runtime assertion |
195 | /// is made that `v` contains no 0 bytes, and it requires an actual | |
196 | /// byte vector, not anything that can be converted to one with Into. | |
c34b1796 | 197 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
198 | pub unsafe fn from_vec_unchecked(mut v: Vec<u8>) -> CString { |
199 | v.push(0); | |
62682a34 SL |
200 | CString { inner: v.into_boxed_slice() } |
201 | } | |
202 | ||
203 | /// Retakes ownership of a CString that was transferred to C. | |
204 | /// | |
205 | /// The only appropriate argument is a pointer obtained by calling | |
206 | /// `into_ptr`. The length of the string will be recalculated | |
207 | /// using the pointer. | |
208 | #[unstable(feature = "cstr_memory", reason = "recently added")] | |
209 | // NB: may want to be called from_raw, needs to consider CStr::from_ptr, | |
210 | // Box::from_raw (or whatever it's currently called), and | |
211 | // slice::from_raw_parts | |
212 | pub unsafe fn from_ptr(ptr: *const libc::c_char) -> CString { | |
213 | let len = libc::strlen(ptr) + 1; // Including the NUL byte | |
214 | let slice = slice::from_raw_parts(ptr, len as usize); | |
215 | CString { inner: mem::transmute(slice) } | |
216 | } | |
217 | ||
218 | /// Transfers ownership of the string to a C caller. | |
219 | /// | |
220 | /// The pointer must be returned to Rust and reconstituted using | |
221 | /// `from_ptr` to be properly deallocated. Specifically, one | |
222 | /// should *not* use the standard C `free` function to deallocate | |
223 | /// this string. | |
224 | /// | |
225 | /// Failure to call `from_ptr` will lead to a memory leak. | |
226 | #[unstable(feature = "cstr_memory", reason = "recently added")] | |
227 | // NB: may want to be called into_raw, see comments on from_ptr | |
228 | pub fn into_ptr(self) -> *const libc::c_char { | |
229 | // It is important that the bytes be sized to fit - we need | |
230 | // the capacity to be determinable from the string length, and | |
231 | // shrinking to fit is the only way to be sure. | |
232 | Box::into_raw(self.inner) as *const libc::c_char | |
1a4d82fc JJ |
233 | } |
234 | ||
85aaf69f SL |
235 | /// Returns the contents of this `CString` as a slice of bytes. |
236 | /// | |
237 | /// The returned slice does **not** contain the trailing nul separator and | |
c34b1796 AL |
238 | /// it is guaranteed to not have any interior nul bytes. |
239 | #[stable(feature = "rust1", since = "1.0.0")] | |
1a4d82fc | 240 | pub fn as_bytes(&self) -> &[u8] { |
85aaf69f | 241 | &self.inner[..self.inner.len() - 1] |
1a4d82fc JJ |
242 | } |
243 | ||
85aaf69f SL |
244 | /// Equivalent to the `as_bytes` function except that the returned slice |
245 | /// includes the trailing nul byte. | |
c34b1796 | 246 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc | 247 | pub fn as_bytes_with_nul(&self) -> &[u8] { |
85aaf69f | 248 | &self.inner |
1a4d82fc JJ |
249 | } |
250 | } | |
251 | ||
62682a34 SL |
252 | #[stable(feature = "rust1", since = "1.0.0")] |
253 | impl Clone for CString { | |
254 | fn clone(&self) -> Self { | |
255 | CString { inner: self.inner.to_owned().into_boxed_slice() } | |
256 | } | |
257 | } | |
258 | ||
c34b1796 | 259 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc | 260 | impl Deref for CString { |
85aaf69f | 261 | type Target = CStr; |
1a4d82fc | 262 | |
85aaf69f SL |
263 | fn deref(&self) -> &CStr { |
264 | unsafe { mem::transmute(self.as_bytes_with_nul()) } | |
1a4d82fc JJ |
265 | } |
266 | } | |
267 | ||
85aaf69f SL |
268 | #[stable(feature = "rust1", since = "1.0.0")] |
269 | impl fmt::Debug for CString { | |
1a4d82fc | 270 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
85aaf69f | 271 | fmt::Debug::fmt(&String::from_utf8_lossy(self.as_bytes()), f) |
1a4d82fc JJ |
272 | } |
273 | } | |
274 | ||
85aaf69f SL |
275 | impl NulError { |
276 | /// Returns the position of the nul byte in the slice that was provided to | |
9346a6ac | 277 | /// `CString::new`. |
c34b1796 | 278 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
279 | pub fn nul_position(&self) -> usize { self.0 } |
280 | ||
281 | /// Consumes this error, returning the underlying vector of bytes which | |
282 | /// generated the error in the first place. | |
c34b1796 | 283 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
284 | pub fn into_vec(self) -> Vec<u8> { self.1 } |
285 | } | |
286 | ||
c34b1796 | 287 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
288 | impl Error for NulError { |
289 | fn description(&self) -> &str { "nul byte found in data" } | |
290 | } | |
291 | ||
c34b1796 | 292 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
293 | impl fmt::Display for NulError { |
294 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
295 | write!(f, "nul byte found in provided data at position: {}", self.0) | |
296 | } | |
297 | } | |
298 | ||
c34b1796 AL |
299 | #[stable(feature = "rust1", since = "1.0.0")] |
300 | impl From<NulError> for io::Error { | |
301 | fn from(_: NulError) -> io::Error { | |
85aaf69f | 302 | io::Error::new(io::ErrorKind::InvalidInput, |
c34b1796 | 303 | "data provided contains a nul byte") |
85aaf69f SL |
304 | } |
305 | } | |
306 | ||
85aaf69f | 307 | impl CStr { |
9346a6ac | 308 | /// Casts a raw C string to a safe C string wrapper. |
85aaf69f SL |
309 | /// |
310 | /// This function will cast the provided `ptr` to the `CStr` wrapper which | |
311 | /// allows inspection and interoperation of non-owned C strings. This method | |
312 | /// is unsafe for a number of reasons: | |
313 | /// | |
314 | /// * There is no guarantee to the validity of `ptr` | |
315 | /// * The returned lifetime is not guaranteed to be the actual lifetime of | |
316 | /// `ptr` | |
317 | /// * There is no guarantee that the memory pointed to by `ptr` contains a | |
318 | /// valid nul terminator byte at the end of the string. | |
319 | /// | |
320 | /// > **Note**: This operation is intended to be a 0-cost cast but it is | |
321 | /// > currently implemented with an up-front calculation of the length of | |
322 | /// > the string. This is not guaranteed to always be the case. | |
323 | /// | |
c34b1796 | 324 | /// # Examples |
85aaf69f SL |
325 | /// |
326 | /// ```no_run | |
c34b1796 | 327 | /// # #![feature(libc)] |
85aaf69f SL |
328 | /// # extern crate libc; |
329 | /// # fn main() { | |
330 | /// use std::ffi::CStr; | |
331 | /// use std::str; | |
332 | /// use libc; | |
333 | /// | |
334 | /// extern { | |
335 | /// fn my_string() -> *const libc::c_char; | |
336 | /// } | |
337 | /// | |
338 | /// unsafe { | |
339 | /// let slice = CStr::from_ptr(my_string()); | |
340 | /// println!("string returned: {}", | |
341 | /// str::from_utf8(slice.to_bytes()).unwrap()); | |
342 | /// } | |
343 | /// # } | |
344 | /// ``` | |
c34b1796 | 345 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
346 | pub unsafe fn from_ptr<'a>(ptr: *const libc::c_char) -> &'a CStr { |
347 | let len = libc::strlen(ptr); | |
348 | mem::transmute(slice::from_raw_parts(ptr, len as usize + 1)) | |
349 | } | |
350 | ||
9346a6ac | 351 | /// Returns the inner pointer to this C string. |
85aaf69f SL |
352 | /// |
353 | /// The returned pointer will be valid for as long as `self` is and points | |
c34b1796 | 354 | /// to a contiguous region of memory terminated with a 0 byte to represent |
85aaf69f | 355 | /// the end of the string. |
c34b1796 | 356 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
357 | pub fn as_ptr(&self) -> *const libc::c_char { |
358 | self.inner.as_ptr() | |
359 | } | |
360 | ||
9346a6ac | 361 | /// Converts this C string to a byte slice. |
85aaf69f SL |
362 | /// |
363 | /// This function will calculate the length of this string (which normally | |
364 | /// requires a linear amount of work to be done) and then return the | |
365 | /// resulting slice of `u8` elements. | |
366 | /// | |
367 | /// The returned slice will **not** contain the trailing nul that this C | |
368 | /// string has. | |
369 | /// | |
370 | /// > **Note**: This method is currently implemented as a 0-cost cast, but | |
371 | /// > it is planned to alter its definition in the future to perform the | |
372 | /// > length calculation whenever this method is called. | |
c34b1796 | 373 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
374 | pub fn to_bytes(&self) -> &[u8] { |
375 | let bytes = self.to_bytes_with_nul(); | |
376 | &bytes[..bytes.len() - 1] | |
377 | } | |
378 | ||
9346a6ac | 379 | /// Converts this C string to a byte slice containing the trailing 0 byte. |
85aaf69f SL |
380 | /// |
381 | /// This function is the equivalent of `to_bytes` except that it will retain | |
382 | /// the trailing nul instead of chopping it off. | |
383 | /// | |
384 | /// > **Note**: This method is currently implemented as a 0-cost cast, but | |
385 | /// > it is planned to alter its definition in the future to perform the | |
386 | /// > length calculation whenever this method is called. | |
c34b1796 | 387 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
388 | pub fn to_bytes_with_nul(&self) -> &[u8] { |
389 | unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.inner) } | |
390 | } | |
62682a34 SL |
391 | |
392 | /// Yields a `&str` slice if the `CStr` contains valid UTF-8. | |
393 | /// | |
394 | /// This function will calculate the length of this string and check for | |
395 | /// UTF-8 validity, and then return the `&str` if it's valid. | |
396 | /// | |
397 | /// > **Note**: This method is currently implemented to check for validity | |
398 | /// > after a 0-cost cast, but it is planned to alter its definition in the | |
399 | /// > future to perform the length calculation in addition to the UTF-8 | |
400 | /// > check whenever this method is called. | |
401 | #[unstable(feature = "cstr_to_str", reason = "recently added")] | |
402 | pub fn to_str(&self) -> Result<&str, str::Utf8Error> { | |
403 | // NB: When CStr is changed to perform the length check in .to_bytes() instead of in | |
404 | // from_ptr(), it may be worth considering if this should be rewritten to do the UTF-8 | |
405 | // check inline with the length calculation instead of doing it afterwards. | |
406 | str::from_utf8(self.to_bytes()) | |
407 | } | |
408 | ||
409 | /// Converts a `CStr` into a `Cow<str>`. | |
410 | /// | |
411 | /// This function will calculate the length of this string (which normally | |
412 | /// requires a linear amount of work to be done) and then return the | |
413 | /// resulting slice as a `Cow<str>`, replacing any invalid UTF-8 sequences | |
414 | /// with `U+FFFD REPLACEMENT CHARACTER`. | |
415 | /// | |
416 | /// > **Note**: This method is currently implemented to check for validity | |
417 | /// > after a 0-cost cast, but it is planned to alter its definition in the | |
418 | /// > future to perform the length calculation in addition to the UTF-8 | |
419 | /// > check whenever this method is called. | |
420 | #[unstable(feature = "cstr_to_str", reason = "recently added")] | |
421 | pub fn to_string_lossy(&self) -> Cow<str> { | |
422 | String::from_utf8_lossy(self.to_bytes()) | |
423 | } | |
85aaf69f SL |
424 | } |
425 | ||
c34b1796 | 426 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
427 | impl PartialEq for CStr { |
428 | fn eq(&self, other: &CStr) -> bool { | |
c34b1796 | 429 | self.to_bytes().eq(other.to_bytes()) |
85aaf69f SL |
430 | } |
431 | } | |
c34b1796 | 432 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f | 433 | impl Eq for CStr {} |
c34b1796 | 434 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
435 | impl PartialOrd for CStr { |
436 | fn partial_cmp(&self, other: &CStr) -> Option<Ordering> { | |
437 | self.to_bytes().partial_cmp(&other.to_bytes()) | |
438 | } | |
439 | } | |
c34b1796 | 440 | #[stable(feature = "rust1", since = "1.0.0")] |
85aaf69f SL |
441 | impl Ord for CStr { |
442 | fn cmp(&self, other: &CStr) -> Ordering { | |
443 | self.to_bytes().cmp(&other.to_bytes()) | |
444 | } | |
445 | } | |
446 | ||
1a4d82fc JJ |
447 | #[cfg(test)] |
448 | mod tests { | |
449 | use prelude::v1::*; | |
450 | use super::*; | |
451 | use libc; | |
62682a34 | 452 | use borrow::Cow::{Borrowed, Owned}; |
1a4d82fc JJ |
453 | |
454 | #[test] | |
455 | fn c_to_rust() { | |
456 | let data = b"123\0"; | |
457 | let ptr = data.as_ptr() as *const libc::c_char; | |
458 | unsafe { | |
c34b1796 AL |
459 | assert_eq!(CStr::from_ptr(ptr).to_bytes(), b"123"); |
460 | assert_eq!(CStr::from_ptr(ptr).to_bytes_with_nul(), b"123\0"); | |
1a4d82fc JJ |
461 | } |
462 | } | |
463 | ||
464 | #[test] | |
465 | fn simple() { | |
c34b1796 | 466 | let s = CString::new("1234").unwrap(); |
1a4d82fc JJ |
467 | assert_eq!(s.as_bytes(), b"1234"); |
468 | assert_eq!(s.as_bytes_with_nul(), b"1234\0"); | |
1a4d82fc JJ |
469 | } |
470 | ||
85aaf69f SL |
471 | #[test] |
472 | fn build_with_zero1() { | |
c34b1796 | 473 | assert!(CString::new(&b"\0"[..]).is_err()); |
85aaf69f SL |
474 | } |
475 | #[test] | |
476 | fn build_with_zero2() { | |
477 | assert!(CString::new(vec![0]).is_err()); | |
478 | } | |
1a4d82fc JJ |
479 | |
480 | #[test] | |
481 | fn build_with_zero3() { | |
482 | unsafe { | |
483 | let s = CString::from_vec_unchecked(vec![0]); | |
484 | assert_eq!(s.as_bytes(), b"\0"); | |
485 | } | |
486 | } | |
85aaf69f SL |
487 | |
488 | #[test] | |
489 | fn formatted() { | |
c34b1796 | 490 | let s = CString::new(&b"12"[..]).unwrap(); |
85aaf69f SL |
491 | assert_eq!(format!("{:?}", s), "\"12\""); |
492 | } | |
493 | ||
494 | #[test] | |
495 | fn borrowed() { | |
496 | unsafe { | |
497 | let s = CStr::from_ptr(b"12\0".as_ptr() as *const _); | |
498 | assert_eq!(s.to_bytes(), b"12"); | |
499 | assert_eq!(s.to_bytes_with_nul(), b"12\0"); | |
500 | } | |
501 | } | |
62682a34 SL |
502 | |
503 | #[test] | |
504 | fn to_str() { | |
505 | let data = b"123\xE2\x80\xA6\0"; | |
506 | let ptr = data.as_ptr() as *const libc::c_char; | |
507 | unsafe { | |
508 | assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…")); | |
509 | assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…")); | |
510 | } | |
511 | let data = b"123\xE2\0"; | |
512 | let ptr = data.as_ptr() as *const libc::c_char; | |
513 | unsafe { | |
514 | assert!(CStr::from_ptr(ptr).to_str().is_err()); | |
515 | assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::<str>(format!("123\u{FFFD}"))); | |
516 | } | |
517 | } | |
1a4d82fc | 518 | } |