library: Move CStr
to libcore, and CString
to liballoc
This commit is contained in:
parent
e7575f9670
commit
5bee741a08
@ -731,9 +731,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.70"
|
||||
version = "0.1.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80873f979f0a344a4ade87c2f70d9ccf5720b83b10c97ec7cd745895d021e85a"
|
||||
checksum = "163437f05ca8f29d7e9128ea728dedf5eb620e445fbca273641d3a3050305f23"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"rustc-std-workspace-core",
|
||||
|
@ -326,6 +326,8 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
|
||||
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
|
||||
|
||||
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
|
||||
}
|
||||
|
||||
pub enum GenericRequirement {
|
||||
|
@ -417,6 +417,7 @@ fn register_crate(
|
||||
|
||||
let crate_metadata = CrateMetadata::new(
|
||||
self.sess,
|
||||
&self.cstore,
|
||||
metadata,
|
||||
crate_root,
|
||||
raw_proc_macros,
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Decoding metadata from a single crate's metadata
|
||||
|
||||
use crate::creader::CrateMetadataRef;
|
||||
use crate::creader::{CStore, CrateMetadataRef};
|
||||
use crate::rmeta::table::{FixedSizeEncoding, Table};
|
||||
use crate::rmeta::*;
|
||||
|
||||
@ -1737,6 +1737,7 @@ fn imported_source_files(self, sess: &Session) -> &'a [ImportedSourceFile] {
|
||||
impl CrateMetadata {
|
||||
crate fn new(
|
||||
sess: &Session,
|
||||
cstore: &CStore,
|
||||
blob: MetadataBlob,
|
||||
root: CrateRoot<'static>,
|
||||
raw_proc_macros: Option<&'static [ProcMacro]>,
|
||||
@ -1752,11 +1753,6 @@ impl CrateMetadata {
|
||||
.decode((&blob, sess))
|
||||
.map(|trait_impls| (trait_impls.trait_id, trait_impls.impls))
|
||||
.collect();
|
||||
let incoherent_impls = root
|
||||
.incoherent_impls
|
||||
.decode((&blob, sess))
|
||||
.map(|incoherent_impls| (incoherent_impls.self_ty, incoherent_impls.impls))
|
||||
.collect();
|
||||
let alloc_decoding_state =
|
||||
AllocDecodingState::new(root.interpret_alloc_index.decode(&blob).collect());
|
||||
let dependencies = Lock::new(cnum_map.iter().cloned().collect());
|
||||
@ -1765,11 +1761,11 @@ impl CrateMetadata {
|
||||
// that does not copy any data. It just does some data verification.
|
||||
let def_path_hash_map = root.def_path_hash_map.decode(&blob);
|
||||
|
||||
CrateMetadata {
|
||||
let mut cdata = CrateMetadata {
|
||||
blob,
|
||||
root,
|
||||
trait_impls,
|
||||
incoherent_impls,
|
||||
incoherent_impls: Default::default(),
|
||||
raw_proc_macros,
|
||||
source_map_import_info: OnceCell::new(),
|
||||
def_path_hash_map,
|
||||
@ -1786,7 +1782,17 @@ impl CrateMetadata {
|
||||
hygiene_context: Default::default(),
|
||||
def_key_cache: Default::default(),
|
||||
def_path_hash_cache: Default::default(),
|
||||
}
|
||||
};
|
||||
|
||||
// Need `CrateMetadataRef` to decode `DefId`s in simplified types.
|
||||
cdata.incoherent_impls = cdata
|
||||
.root
|
||||
.incoherent_impls
|
||||
.decode(CrateMetadataRef { cdata: &cdata, cstore })
|
||||
.map(|incoherent_impls| (incoherent_impls.self_ty, incoherent_impls.impls))
|
||||
.collect();
|
||||
|
||||
cdata
|
||||
}
|
||||
|
||||
crate fn dependencies(&self) -> LockGuard<'_, Vec<CrateNum>> {
|
||||
|
@ -644,7 +644,11 @@ fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'t
|
||||
self.assemble_inherent_impl_candidates_for_type(p.def_id());
|
||||
}
|
||||
ty::Adt(def, _) => {
|
||||
self.assemble_inherent_impl_candidates_for_type(def.did());
|
||||
let def_id = def.did();
|
||||
self.assemble_inherent_impl_candidates_for_type(def_id);
|
||||
if Some(def_id) == self.tcx.lang_items().c_str() {
|
||||
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
|
||||
}
|
||||
}
|
||||
ty::Foreign(did) => {
|
||||
self.assemble_inherent_impl_candidates_for_type(did);
|
||||
|
@ -55,7 +55,12 @@ fn visit_item(&mut self, item: &hir::Item<'_>) {
|
||||
let self_ty = self.tcx.type_of(item.def_id);
|
||||
match *self_ty.kind() {
|
||||
ty::Adt(def, _) => {
|
||||
self.check_def_id(item, def.did());
|
||||
let def_id = def.did();
|
||||
if !def_id.is_local() && Some(def_id) == self.tcx.lang_items().c_str() {
|
||||
self.check_primitive_impl(item.def_id, self_ty, items, ty.span)
|
||||
} else {
|
||||
self.check_def_id(item, def_id);
|
||||
}
|
||||
}
|
||||
ty::Foreign(did) => {
|
||||
self.check_def_id(item, did);
|
||||
|
@ -1,25 +1,23 @@
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::ascii;
|
||||
use crate::borrow::{Borrow, Cow};
|
||||
use crate::cmp::Ordering;
|
||||
use crate::error::Error;
|
||||
use crate::fmt::{self, Write};
|
||||
use crate::io;
|
||||
use crate::mem;
|
||||
use crate::num::NonZeroU8;
|
||||
use crate::ops;
|
||||
use crate::os::raw::c_char;
|
||||
use crate::ptr;
|
||||
use crate::borrow::{Cow, ToOwned};
|
||||
use crate::boxed::Box;
|
||||
use crate::rc::Rc;
|
||||
use crate::slice;
|
||||
use crate::str::{self, Utf8Error};
|
||||
use crate::slice::hack::into_vec;
|
||||
use crate::string::String;
|
||||
use crate::sync::Arc;
|
||||
use crate::sys;
|
||||
use crate::sys_common::memchr;
|
||||
use crate::vec::Vec;
|
||||
use core::borrow::Borrow;
|
||||
use core::ffi::{c_char, CStr};
|
||||
use core::fmt;
|
||||
use core::mem;
|
||||
use core::num::NonZeroU8;
|
||||
use core::ops;
|
||||
use core::ptr;
|
||||
use core::slice;
|
||||
use core::slice::memchr;
|
||||
use core::str::{self, Utf8Error};
|
||||
|
||||
/// A type representing an owned, C-compatible, nul-terminated string with no nul bytes in the
|
||||
/// middle.
|
||||
@ -116,90 +114,6 @@ pub struct CString {
|
||||
inner: Box<[u8]>,
|
||||
}
|
||||
|
||||
/// Representation of a borrowed C string.
|
||||
///
|
||||
/// This type represents a borrowed reference to a nul-terminated
|
||||
/// array of bytes. It can be constructed safely from a <code>&[[u8]]</code>
|
||||
/// slice, or unsafely from a raw `*const c_char`. It can then be
|
||||
/// converted to a Rust <code>&[str]</code> by performing UTF-8 validation, or
|
||||
/// into an owned [`CString`].
|
||||
///
|
||||
/// `&CStr` is to [`CString`] as <code>&[str]</code> is to [`String`]: the former
|
||||
/// in each pair are borrowed references; the latter are owned
|
||||
/// strings.
|
||||
///
|
||||
/// Note that this structure is **not** `repr(C)` and is not recommended to be
|
||||
/// placed in the signatures of FFI functions. Instead, safe wrappers of FFI
|
||||
/// functions may leverage the unsafe [`CStr::from_ptr`] constructor to provide
|
||||
/// a safe interface to other consumers.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Inspecting a foreign C string:
|
||||
///
|
||||
/// ```ignore (extern-declaration)
|
||||
/// use std::ffi::CStr;
|
||||
/// use std::os::raw::c_char;
|
||||
///
|
||||
/// extern "C" { fn my_string() -> *const c_char; }
|
||||
///
|
||||
/// unsafe {
|
||||
/// let slice = CStr::from_ptr(my_string());
|
||||
/// println!("string buffer size without nul terminator: {}", slice.to_bytes().len());
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Passing a Rust-originating C string:
|
||||
///
|
||||
/// ```ignore (extern-declaration)
|
||||
/// use std::ffi::{CString, CStr};
|
||||
/// use std::os::raw::c_char;
|
||||
///
|
||||
/// fn work(data: &CStr) {
|
||||
/// extern "C" { fn work_with(data: *const c_char); }
|
||||
///
|
||||
/// unsafe { work_with(data.as_ptr()) }
|
||||
/// }
|
||||
///
|
||||
/// let s = CString::new("data data data data").expect("CString::new failed");
|
||||
/// work(&s);
|
||||
/// ```
|
||||
///
|
||||
/// Converting a foreign C string into a Rust [`String`]:
|
||||
///
|
||||
/// ```ignore (extern-declaration)
|
||||
/// use std::ffi::CStr;
|
||||
/// use std::os::raw::c_char;
|
||||
///
|
||||
/// extern "C" { fn my_string() -> *const c_char; }
|
||||
///
|
||||
/// fn my_string_safe() -> String {
|
||||
/// unsafe {
|
||||
/// CStr::from_ptr(my_string()).to_string_lossy().into_owned()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// println!("string: {}", my_string_safe());
|
||||
/// ```
|
||||
///
|
||||
/// [str]: prim@str "str"
|
||||
#[derive(Hash)]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "CStr")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
// FIXME:
|
||||
// `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies
|
||||
// on `CStr` being layout-compatible with `[u8]`.
|
||||
// When attribute privacy is implemented, `CStr` should be annotated as `#[repr(transparent)]`.
|
||||
// Anyway, `CStr` representation and layout are considered implementation detail, are
|
||||
// not documented and must not be relied upon.
|
||||
pub struct CStr {
|
||||
// FIXME: this should not be represented with a DST slice but rather with
|
||||
// just a raw `c_char` along with some form of marker to make
|
||||
// this an unsized type. Essentially `sizeof(&CStr)` should be the
|
||||
// same as `sizeof(&c_char)` but `CStr` should be an unsized type.
|
||||
inner: [c_char],
|
||||
}
|
||||
|
||||
/// An error indicating that an interior nul byte was found.
|
||||
///
|
||||
/// While Rust strings may contain nul bytes in the middle, C strings
|
||||
@ -219,25 +133,10 @@ pub struct CStr {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct NulError(usize, Vec<u8>);
|
||||
|
||||
/// An error indicating that a nul byte was not in the expected position.
|
||||
///
|
||||
/// The slice used to create a [`CStr`] must have one and only one nul byte,
|
||||
/// positioned at the end.
|
||||
///
|
||||
/// This error is created by the [`CStr::from_bytes_with_nul`] method.
|
||||
/// See its documentation for more.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::{CStr, FromBytesWithNulError};
|
||||
///
|
||||
/// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err();
|
||||
/// ```
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
pub struct FromBytesWithNulError {
|
||||
kind: FromBytesWithNulErrorKind,
|
||||
enum FromBytesWithNulErrorKind {
|
||||
InteriorNul(usize),
|
||||
NotNulTerminated,
|
||||
}
|
||||
|
||||
/// An error indicating that a nul byte was not in the expected position.
|
||||
@ -262,21 +161,6 @@ pub struct FromVecWithNulError {
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
enum FromBytesWithNulErrorKind {
|
||||
InteriorNul(usize),
|
||||
NotNulTerminated,
|
||||
}
|
||||
|
||||
impl FromBytesWithNulError {
|
||||
fn interior_nul(pos: usize) -> FromBytesWithNulError {
|
||||
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) }
|
||||
}
|
||||
fn not_nul_terminated() -> FromBytesWithNulError {
|
||||
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
||||
impl FromVecWithNulError {
|
||||
/// Returns a slice of [`u8`]s bytes that were attempted to convert to a [`CString`].
|
||||
@ -328,27 +212,6 @@ pub fn into_bytes(self) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An error indicating that no nul byte was present.
|
||||
///
|
||||
/// A slice used to create a [`CStr`] must contain a nul byte somewhere
|
||||
/// within the slice.
|
||||
///
|
||||
/// This error is created by the [`CStr::from_bytes_until_nul`] method.
|
||||
///
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
pub struct FromBytesUntilNulError(());
|
||||
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
impl Error for FromBytesUntilNulError {}
|
||||
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
impl fmt::Display for FromBytesUntilNulError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "data provided does not contain a nul")
|
||||
}
|
||||
}
|
||||
|
||||
/// An error indicating invalid UTF-8 when converting a [`CString`] into a [`String`].
|
||||
///
|
||||
/// `CString` is just a wrapper over a buffer of bytes with a nul terminator;
|
||||
@ -536,7 +399,11 @@ pub unsafe fn from_raw(ptr: *mut c_char) -> CString {
|
||||
// information about the size of the allocation is correct on Rust's
|
||||
// side.
|
||||
unsafe {
|
||||
let len = sys::strlen(ptr) + 1; // Including the NUL byte
|
||||
extern "C" {
|
||||
/// Provided by libc or compiler_builtins.
|
||||
fn strlen(s: *const c_char) -> usize;
|
||||
}
|
||||
let len = strlen(ptr) + 1; // Including the NUL byte
|
||||
let slice = slice::from_raw_parts_mut(ptr, len as usize);
|
||||
CString { inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]) }
|
||||
}
|
||||
@ -626,7 +493,7 @@ pub fn into_string(self) -> Result<String, IntoStringError> {
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
let mut vec = self.into_inner().into_vec();
|
||||
let mut vec = into_vec(self.into_inner());
|
||||
let _nul = vec.pop();
|
||||
debug_assert_eq!(_nul, Some(0u8));
|
||||
vec
|
||||
@ -647,7 +514,7 @@ pub fn into_bytes(self) -> Vec<u8> {
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
||||
pub fn into_bytes_with_nul(self) -> Vec<u8> {
|
||||
self.into_inner().into_vec()
|
||||
into_vec(self.into_inner())
|
||||
}
|
||||
|
||||
/// Returns the contents of this `CString` as a slice of bytes.
|
||||
@ -842,7 +709,7 @@ impl ops::Deref for CString {
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &CStr {
|
||||
unsafe { CStr::_from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) }
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -864,25 +731,6 @@ fn from(s: CString) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstr_debug", since = "1.3.0")]
|
||||
impl fmt::Debug for CStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "\"")?;
|
||||
for byte in self.to_bytes().iter().flat_map(|&b| ascii::escape_default(b)) {
|
||||
f.write_char(byte as char)?;
|
||||
}
|
||||
write!(f, "\"")
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstr_default", since = "1.10.0")]
|
||||
impl Default for &CStr {
|
||||
fn default() -> Self {
|
||||
const SLICE: &[c_char] = &[0];
|
||||
unsafe { CStr::from_ptr(SLICE.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstr_default", since = "1.10.0")]
|
||||
impl Default for CString {
|
||||
/// Creates an empty `CString`.
|
||||
@ -910,6 +758,7 @@ fn from(s: Cow<'a, CStr>) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[stable(feature = "box_from_c_str", since = "1.17.0")]
|
||||
impl From<&CStr> for Box<CStr> {
|
||||
/// Converts a `&CStr` into a `Box<CStr>`,
|
||||
@ -938,7 +787,8 @@ impl From<Box<CStr>> for CString {
|
||||
/// Converts a <code>[Box]<[CStr]></code> into a [`CString`] without copying or allocating.
|
||||
#[inline]
|
||||
fn from(s: Box<CStr>) -> CString {
|
||||
s.into_c_string()
|
||||
let raw = Box::into_raw(s) as *mut [u8];
|
||||
CString { inner: unsafe { Box::from_raw(raw) } }
|
||||
}
|
||||
}
|
||||
|
||||
@ -964,6 +814,7 @@ fn from(v: Vec<NonZeroU8>) -> CString {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[stable(feature = "more_box_slice_clone", since = "1.29.0")]
|
||||
impl Clone for Box<CStr> {
|
||||
#[inline]
|
||||
@ -1052,6 +903,7 @@ fn from(s: &CStr) -> Rc<CStr> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[stable(feature = "default_box_extra", since = "1.17.0")]
|
||||
impl Default for Box<CStr> {
|
||||
fn default() -> Box<CStr> {
|
||||
@ -1099,14 +951,6 @@ pub fn into_vec(self) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Error for NulError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
"nul byte found in data"
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl fmt::Display for NulError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@ -1114,42 +958,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl From<NulError> for io::Error {
|
||||
/// Converts a [`NulError`] into a [`io::Error`].
|
||||
fn from(_: NulError) -> io::Error {
|
||||
io::const_io_error!(io::ErrorKind::InvalidInput, "data provided contains a nul byte")
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
|
||||
impl Error for FromBytesWithNulError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
match self.kind {
|
||||
FromBytesWithNulErrorKind::InteriorNul(..) => {
|
||||
"data provided contains an interior nul byte"
|
||||
}
|
||||
FromBytesWithNulErrorKind::NotNulTerminated => "data provided is not nul terminated",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
|
||||
impl fmt::Display for FromBytesWithNulError {
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.description())?;
|
||||
if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind {
|
||||
write!(f, " at byte pos {pos}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
||||
impl Error for FromVecWithNulError {}
|
||||
|
||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
||||
impl fmt::Display for FromVecWithNulError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@ -1179,18 +987,18 @@ pub fn into_cstring(self) -> CString {
|
||||
pub fn utf8_error(&self) -> Utf8Error {
|
||||
self.error
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "cstr_internals", issue = "none")]
|
||||
pub fn __source(&self) -> &Utf8Error {
|
||||
&self.error
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
||||
impl Error for IntoStringError {
|
||||
#[allow(deprecated)]
|
||||
impl IntoStringError {
|
||||
fn description(&self) -> &str {
|
||||
"C string contained non-utf8 bytes"
|
||||
}
|
||||
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(&self.error)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
||||
@ -1201,327 +1009,50 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
}
|
||||
|
||||
impl CStr {
|
||||
/// Wraps a raw C string with a safe C string wrapper.
|
||||
///
|
||||
/// This function will wrap the provided `ptr` with a `CStr` wrapper, which
|
||||
/// allows inspection and interoperation of non-owned C strings. The total
|
||||
/// size of the raw C string must be smaller than `isize::MAX` **bytes**
|
||||
/// in memory due to calling the `slice::from_raw_parts` function.
|
||||
/// This method is unsafe for a number of reasons:
|
||||
///
|
||||
/// * There is no guarantee to the validity of `ptr`.
|
||||
/// * The returned lifetime is not guaranteed to be the actual lifetime of
|
||||
/// `ptr`.
|
||||
/// * There is no guarantee that the memory pointed to by `ptr` contains a
|
||||
/// valid nul terminator byte at the end of the string.
|
||||
/// * It is not guaranteed that the memory pointed by `ptr` won't change
|
||||
/// before the `CStr` has been destroyed.
|
||||
///
|
||||
/// > **Note**: This operation is intended to be a 0-cost cast but it is
|
||||
/// > currently implemented with an up-front calculation of the length of
|
||||
/// > the string. This is not guaranteed to always be the case.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore (extern-declaration)
|
||||
/// # fn main() {
|
||||
/// use std::ffi::CStr;
|
||||
/// use std::os::raw::c_char;
|
||||
///
|
||||
/// extern "C" {
|
||||
/// fn my_string() -> *const c_char;
|
||||
/// }
|
||||
///
|
||||
/// unsafe {
|
||||
/// let slice = CStr::from_ptr(my_string());
|
||||
/// println!("string returned: {}", slice.to_str().unwrap());
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
|
||||
// SAFETY: The caller has provided a pointer that points to a valid C
|
||||
// string with a NUL terminator of size less than `isize::MAX`, whose
|
||||
// content remain valid and doesn't change for the lifetime of the
|
||||
// returned `CStr`.
|
||||
//
|
||||
// Thus computing the length is fine (a NUL byte exists), the call to
|
||||
// from_raw_parts is safe because we know the length is at most `isize::MAX`, meaning
|
||||
// the call to `from_bytes_with_nul_unchecked` is correct.
|
||||
//
|
||||
// The cast from c_char to u8 is ok because a c_char is always one byte.
|
||||
unsafe {
|
||||
let len = sys::strlen(ptr);
|
||||
let ptr = ptr as *const u8;
|
||||
Self::_from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1))
|
||||
}
|
||||
#[stable(feature = "cstr_borrow", since = "1.3.0")]
|
||||
impl ToOwned for CStr {
|
||||
type Owned = CString;
|
||||
|
||||
fn to_owned(&self) -> CString {
|
||||
CString { inner: self.to_bytes_with_nul().into() }
|
||||
}
|
||||
|
||||
/// Creates a C string wrapper from a byte slice.
|
||||
///
|
||||
/// This method will create a `CStr` from any byte slice that contains at
|
||||
/// least one nul byte. The caller does not need to know or specify where
|
||||
/// the nul byte is located.
|
||||
///
|
||||
/// If the first byte is a nul character, this method will return an
|
||||
/// empty `CStr`. If multiple nul characters are present, the `CStr` will
|
||||
/// end at the first one.
|
||||
///
|
||||
/// If the slice only has a single nul byte at the end, this method is
|
||||
/// equivalent to [`CStr::from_bytes_with_nul`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// #![feature(cstr_from_bytes_until_nul)]
|
||||
///
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let mut buffer = [0u8; 16];
|
||||
/// unsafe {
|
||||
/// // Here we might call an unsafe C function that writes a string
|
||||
/// // into the buffer.
|
||||
/// let buf_ptr = buffer.as_mut_ptr();
|
||||
/// buf_ptr.write_bytes(b'A', 8);
|
||||
/// }
|
||||
/// // Attempt to extract a C nul-terminated string from the buffer.
|
||||
/// let c_str = CStr::from_bytes_until_nul(&buffer[..]).unwrap();
|
||||
/// assert_eq!(c_str.to_str().unwrap(), "AAAAAAAA");
|
||||
/// ```
|
||||
///
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
pub fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> {
|
||||
let nul_pos = memchr::memchr(0, bytes);
|
||||
match nul_pos {
|
||||
Some(nul_pos) => {
|
||||
// SAFETY: We know there is a nul byte at nul_pos, so this slice
|
||||
// (ending at the nul byte) is a well-formed C string.
|
||||
let subslice = &bytes[..nul_pos + 1];
|
||||
Ok(unsafe { CStr::from_bytes_with_nul_unchecked(subslice) })
|
||||
}
|
||||
None => Err(FromBytesUntilNulError(())),
|
||||
}
|
||||
fn clone_into(&self, target: &mut CString) {
|
||||
let mut b = into_vec(mem::take(&mut target.inner));
|
||||
self.to_bytes_with_nul().clone_into(&mut b);
|
||||
target.inner = b.into_boxed_slice();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a C string wrapper from a byte slice.
|
||||
///
|
||||
/// This function will cast the provided `bytes` to a `CStr`
|
||||
/// wrapper after ensuring that the byte slice is nul-terminated
|
||||
/// and does not contain any interior nul bytes.
|
||||
///
|
||||
/// If the nul byte may not be at the end,
|
||||
/// [`CStr::from_bytes_until_nul`] can be used instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"hello\0");
|
||||
/// assert!(cstr.is_ok());
|
||||
/// ```
|
||||
///
|
||||
/// Creating a `CStr` without a trailing nul terminator is an error:
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"hello");
|
||||
/// assert!(cstr.is_err());
|
||||
/// ```
|
||||
///
|
||||
/// Creating a `CStr` with an interior nul byte is an error:
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"he\0llo\0");
|
||||
/// assert!(cstr.is_err());
|
||||
/// ```
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {
|
||||
let nul_pos = memchr::memchr(0, bytes);
|
||||
match nul_pos {
|
||||
Some(nul_pos) if nul_pos + 1 == bytes.len() => {
|
||||
// SAFETY: We know there is only one nul byte, at the end
|
||||
// of the byte slice.
|
||||
Ok(unsafe { Self::_from_bytes_with_nul_unchecked(bytes) })
|
||||
}
|
||||
Some(nul_pos) => Err(FromBytesWithNulError::interior_nul(nul_pos)),
|
||||
None => Err(FromBytesWithNulError::not_nul_terminated()),
|
||||
}
|
||||
#[stable(feature = "cstring_asref", since = "1.7.0")]
|
||||
impl From<&CStr> for CString {
|
||||
fn from(s: &CStr) -> CString {
|
||||
s.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsafely creates a C string wrapper from a byte slice.
|
||||
///
|
||||
/// This function will cast the provided `bytes` to a `CStr` wrapper without
|
||||
/// performing any sanity checks. The provided slice **must** be nul-terminated
|
||||
/// and not contain any interior nul bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::{CStr, CString};
|
||||
///
|
||||
/// unsafe {
|
||||
/// let cstring = CString::new("hello").expect("CString::new failed");
|
||||
/// let cstr = CStr::from_bytes_with_nul_unchecked(cstring.to_bytes_with_nul());
|
||||
/// assert_eq!(cstr, &*cstring);
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
#[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")]
|
||||
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
|
||||
// We're in a const fn, so this is the best we can do
|
||||
debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0);
|
||||
unsafe { Self::_from_bytes_with_nul_unchecked(bytes) }
|
||||
}
|
||||
#[stable(feature = "cstring_asref", since = "1.7.0")]
|
||||
impl ops::Index<ops::RangeFull> for CString {
|
||||
type Output = CStr;
|
||||
|
||||
#[inline]
|
||||
const unsafe fn _from_bytes_with_nul_unchecked(bytes: &[u8]) -> &Self {
|
||||
// SAFETY: Casting to CStr is safe because its internal representation
|
||||
// is a [u8] too (safe only inside std).
|
||||
// Dereferencing the obtained pointer is safe because it comes from a
|
||||
// reference. Making a reference is then safe because its lifetime
|
||||
// is bound by the lifetime of the given `bytes`.
|
||||
unsafe { &*(bytes as *const [u8] as *const Self) }
|
||||
fn index(&self, _index: ops::RangeFull) -> &CStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the inner pointer to this C string.
|
||||
///
|
||||
/// The returned pointer will be valid for as long as `self` is, and points
|
||||
/// to a contiguous region of memory terminated with a 0 byte to represent
|
||||
/// the end of the string.
|
||||
///
|
||||
/// **WARNING**
|
||||
///
|
||||
/// The returned pointer is read-only; writing to it (including passing it
|
||||
/// to C code that writes to it) causes undefined behavior.
|
||||
///
|
||||
/// It is your responsibility to make sure that the underlying memory is not
|
||||
/// freed too early. For example, the following code will cause undefined
|
||||
/// behavior when `ptr` is used inside the `unsafe` block:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #![allow(unused_must_use)] #![allow(temporary_cstring_as_ptr)]
|
||||
/// use std::ffi::CString;
|
||||
///
|
||||
/// let ptr = CString::new("Hello").expect("CString::new failed").as_ptr();
|
||||
/// unsafe {
|
||||
/// // `ptr` is dangling
|
||||
/// *ptr;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This happens because the pointer returned by `as_ptr` does not carry any
|
||||
/// lifetime information and the [`CString`] is deallocated immediately after
|
||||
/// the `CString::new("Hello").expect("CString::new failed").as_ptr()`
|
||||
/// expression is evaluated.
|
||||
/// To fix the problem, bind the `CString` to a local variable:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #![allow(unused_must_use)]
|
||||
/// use std::ffi::CString;
|
||||
///
|
||||
/// let hello = CString::new("Hello").expect("CString::new failed");
|
||||
/// let ptr = hello.as_ptr();
|
||||
/// unsafe {
|
||||
/// // `ptr` is valid because `hello` is in scope
|
||||
/// *ptr;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This way, the lifetime of the [`CString`] in `hello` encompasses
|
||||
/// the lifetime of `ptr` and the `unsafe` block.
|
||||
#[stable(feature = "cstring_asref", since = "1.7.0")]
|
||||
impl AsRef<CStr> for CString {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_str_as_ptr", since = "1.32.0")]
|
||||
pub const fn as_ptr(&self) -> *const c_char {
|
||||
self.inner.as_ptr()
|
||||
}
|
||||
|
||||
/// Converts this C string to a byte slice.
|
||||
///
|
||||
/// The returned slice will **not** contain the trailing nul terminator that this C
|
||||
/// string has.
|
||||
///
|
||||
/// > **Note**: This method is currently implemented as a constant-time
|
||||
/// > cast, but it is planned to alter its definition in the future to
|
||||
/// > perform the length calculation whenever this method is called.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
|
||||
/// assert_eq!(cstr.to_bytes(), b"foo");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn to_bytes(&self) -> &[u8] {
|
||||
let bytes = self.to_bytes_with_nul();
|
||||
// SAFETY: to_bytes_with_nul returns slice with length at least 1
|
||||
unsafe { bytes.get_unchecked(..bytes.len() - 1) }
|
||||
}
|
||||
|
||||
/// Converts this C string to a byte slice containing the trailing 0 byte.
|
||||
///
|
||||
/// This function is the equivalent of [`CStr::to_bytes`] except that it
|
||||
/// will retain the trailing nul terminator instead of chopping it off.
|
||||
///
|
||||
/// > **Note**: This method is currently implemented as a 0-cost cast, but
|
||||
/// > it is planned to alter its definition in the future to perform the
|
||||
/// > length calculation whenever this method is called.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
|
||||
/// assert_eq!(cstr.to_bytes_with_nul(), b"foo\0");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn to_bytes_with_nul(&self) -> &[u8] {
|
||||
unsafe { &*(&self.inner as *const [c_char] as *const [u8]) }
|
||||
}
|
||||
|
||||
/// Yields a <code>&[str]</code> slice if the `CStr` contains valid UTF-8.
|
||||
///
|
||||
/// If the contents of the `CStr` are valid UTF-8 data, this
|
||||
/// function will return the corresponding <code>&[str]</code> slice. Otherwise,
|
||||
/// it will return an error with details of where UTF-8 validation failed.
|
||||
///
|
||||
/// [str]: prim@str "str"
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
|
||||
/// assert_eq!(cstr.to_str(), Ok("foo"));
|
||||
/// ```
|
||||
#[stable(feature = "cstr_to_str", since = "1.4.0")]
|
||||
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
|
||||
// N.B., when `CStr` is changed to perform the length check in `.to_bytes()`
|
||||
// instead of in `from_ptr()`, it may be worth considering if this should
|
||||
// be rewritten to do the UTF-8 check inline with the length calculation
|
||||
// instead of doing it afterwards.
|
||||
str::from_utf8(self.to_bytes())
|
||||
fn as_ref(&self) -> &CStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "cstr_internals", issue = "none")]
|
||||
pub trait CStrExt {
|
||||
/// Converts a `CStr` into a <code>[Cow]<[str]></code>.
|
||||
///
|
||||
/// If the contents of the `CStr` are valid UTF-8 data, this
|
||||
@ -1565,6 +1096,83 @@ pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[stable(feature = "cstr_to_str", since = "1.4.0")]
|
||||
fn to_string_lossy(&self) -> Cow<'_, str>;
|
||||
|
||||
/// Converts a <code>[Box]<[CStr]></code> into a [`CString`] without copying or allocating.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CString;
|
||||
///
|
||||
/// let c_string = CString::new(b"foo".to_vec()).expect("CString::new failed");
|
||||
/// let boxed = c_string.into_boxed_c_str();
|
||||
/// assert_eq!(boxed.into_c_string(), CString::new("foo").expect("CString::new failed"));
|
||||
/// ```
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
#[stable(feature = "into_boxed_c_str", since = "1.20.0")]
|
||||
fn into_c_string(self: Box<Self>) -> CString;
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
#[unstable(feature = "cstr_internals", issue = "none")]
|
||||
impl CStrExt for CStr {
|
||||
fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
String::from_utf8_lossy(self.to_bytes())
|
||||
}
|
||||
|
||||
fn into_c_string(self: Box<Self>) -> CString {
|
||||
CString::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
#[cfg(not(test))]
|
||||
impl CStr {
|
||||
/// Converts a `CStr` into a <code>[Cow]<[str]></code>.
|
||||
///
|
||||
/// If the contents of the `CStr` are valid UTF-8 data, this
|
||||
/// function will return a <code>[Cow]::[Borrowed]\(&[str])</code>
|
||||
/// with the corresponding <code>&[str]</code> slice. Otherwise, it will
|
||||
/// replace any invalid UTF-8 sequences with
|
||||
/// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a
|
||||
/// <code>[Cow]::[Owned]\(&[str])</code> with the result.
|
||||
///
|
||||
/// [str]: prim@str "str"
|
||||
/// [Borrowed]: Cow::Borrowed
|
||||
/// [Owned]: Cow::Owned
|
||||
/// [U+FFFD]: core::char::REPLACEMENT_CHARACTER "std::char::REPLACEMENT_CHARACTER"
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Calling `to_string_lossy` on a `CStr` containing valid UTF-8:
|
||||
///
|
||||
/// ```
|
||||
/// use std::borrow::Cow;
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"Hello World\0")
|
||||
/// .expect("CStr::from_bytes_with_nul failed");
|
||||
/// assert_eq!(cstr.to_string_lossy(), Cow::Borrowed("Hello World"));
|
||||
/// ```
|
||||
///
|
||||
/// Calling `to_string_lossy` on a `CStr` containing invalid UTF-8:
|
||||
///
|
||||
/// ```
|
||||
/// use std::borrow::Cow;
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"Hello \xF0\x90\x80World\0")
|
||||
/// .expect("CStr::from_bytes_with_nul failed");
|
||||
/// assert_eq!(
|
||||
/// cstr.to_string_lossy(),
|
||||
/// Cow::Owned(String::from("Hello <20>World")) as Cow<'_, str>
|
||||
/// );
|
||||
/// ```
|
||||
#[rustc_allow_incoherent_impl]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[stable(feature = "cstr_to_str", since = "1.4.0")]
|
||||
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
String::from_utf8_lossy(self.to_bytes())
|
||||
}
|
||||
@ -1580,101 +1188,10 @@ pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
/// let boxed = c_string.into_boxed_c_str();
|
||||
/// assert_eq!(boxed.into_c_string(), CString::new("foo").expect("CString::new failed"));
|
||||
/// ```
|
||||
#[rustc_allow_incoherent_impl]
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
#[stable(feature = "into_boxed_c_str", since = "1.20.0")]
|
||||
pub fn into_c_string(self: Box<CStr>) -> CString {
|
||||
let raw = Box::into_raw(self) as *mut [u8];
|
||||
CString { inner: unsafe { Box::from_raw(raw) } }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialEq for CStr {
|
||||
fn eq(&self, other: &CStr) -> bool {
|
||||
self.to_bytes().eq(other.to_bytes())
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Eq for CStr {}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialOrd for CStr {
|
||||
fn partial_cmp(&self, other: &CStr) -> Option<Ordering> {
|
||||
self.to_bytes().partial_cmp(&other.to_bytes())
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Ord for CStr {
|
||||
fn cmp(&self, other: &CStr) -> Ordering {
|
||||
self.to_bytes().cmp(&other.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstr_borrow", since = "1.3.0")]
|
||||
impl ToOwned for CStr {
|
||||
type Owned = CString;
|
||||
|
||||
fn to_owned(&self) -> CString {
|
||||
CString { inner: self.to_bytes_with_nul().into() }
|
||||
}
|
||||
|
||||
fn clone_into(&self, target: &mut CString) {
|
||||
let mut b = Vec::from(mem::take(&mut target.inner));
|
||||
self.to_bytes_with_nul().clone_into(&mut b);
|
||||
target.inner = b.into_boxed_slice();
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstring_asref", since = "1.7.0")]
|
||||
impl From<&CStr> for CString {
|
||||
/// Copies the contents of the `&CStr` into a newly allocated `CString`.
|
||||
fn from(s: &CStr) -> CString {
|
||||
s.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstring_asref", since = "1.7.0")]
|
||||
impl ops::Index<ops::RangeFull> for CString {
|
||||
type Output = CStr;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, _index: ops::RangeFull) -> &CStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstr_range_from", since = "1.47.0")]
|
||||
impl ops::Index<ops::RangeFrom<usize>> for CStr {
|
||||
type Output = CStr;
|
||||
|
||||
fn index(&self, index: ops::RangeFrom<usize>) -> &CStr {
|
||||
let bytes = self.to_bytes_with_nul();
|
||||
// we need to manually check the starting index to account for the null
|
||||
// byte, since otherwise we could get an empty string that doesn't end
|
||||
// in a null.
|
||||
if index.start < bytes.len() {
|
||||
unsafe { CStr::_from_bytes_with_nul_unchecked(&bytes[index.start..]) }
|
||||
} else {
|
||||
panic!(
|
||||
"index out of bounds: the len is {} but the index is {}",
|
||||
bytes.len(),
|
||||
index.start
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstring_asref", since = "1.7.0")]
|
||||
impl AsRef<CStr> for CStr {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &CStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstring_asref", since = "1.7.0")]
|
||||
impl AsRef<CStr> for CString {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &CStr {
|
||||
self
|
||||
pub fn into_c_string(self: Box<Self>) -> CString {
|
||||
CString::from(self)
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
use super::*;
|
||||
use crate::borrow::Cow::{Borrowed, Owned};
|
||||
use crate::collections::hash_map::DefaultHasher;
|
||||
use crate::hash::{Hash, Hasher};
|
||||
use crate::os::raw::c_char;
|
||||
use crate::rc::Rc;
|
||||
use crate::sync::Arc;
|
||||
use core::assert_matches::assert_matches;
|
||||
use core::ffi::FromBytesUntilNulError;
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
#[allow(deprecated)]
|
||||
use core::hash::SipHasher13 as DefaultHasher;
|
||||
|
||||
#[test]
|
||||
fn c_to_rust() {
|
||||
@ -47,22 +49,6 @@ fn borrowed() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
let data = b"123\xE2\x80\xA6\0";
|
||||
let ptr = data.as_ptr() as *const c_char;
|
||||
unsafe {
|
||||
assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…"));
|
||||
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…"));
|
||||
}
|
||||
let data = b"123\xE2\0";
|
||||
let ptr = data.as_ptr() as *const c_char;
|
||||
unsafe {
|
||||
assert!(CStr::from_ptr(ptr).to_str().is_err());
|
||||
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::<str>(format!("123\u{FFFD}")));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_owned() {
|
||||
let data = b"123\0";
|
||||
@ -78,9 +64,11 @@ fn equal_hash() {
|
||||
let ptr = data.as_ptr() as *const c_char;
|
||||
let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) };
|
||||
|
||||
#[allow(deprecated)]
|
||||
let mut s = DefaultHasher::new();
|
||||
cstr.hash(&mut s);
|
||||
let cstr_hash = s.finish();
|
||||
#[allow(deprecated)]
|
||||
let mut s = DefaultHasher::new();
|
||||
CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s);
|
||||
let cstring_hash = s.finish();
|
||||
@ -122,11 +110,11 @@ fn cstr_from_bytes_until_nul() {
|
||||
// Test an empty slice. This should fail because it
|
||||
// does not contain a nul byte.
|
||||
let b = b"";
|
||||
assert_eq!(CStr::from_bytes_until_nul(&b[..]), Err(FromBytesUntilNulError(())));
|
||||
assert_matches!(CStr::from_bytes_until_nul(&b[..]), Err(FromBytesUntilNulError { .. }));
|
||||
|
||||
// Test a non-empty slice, that does not contain a nul byte.
|
||||
let b = b"hello";
|
||||
assert_eq!(CStr::from_bytes_until_nul(&b[..]), Err(FromBytesUntilNulError(())));
|
||||
assert_matches!(CStr::from_bytes_until_nul(&b[..]), Err(FromBytesUntilNulError { .. }));
|
||||
|
||||
// Test an empty nul-terminated string
|
||||
let b = b"\0";
|
91
library/alloc/src/ffi/mod.rs
Normal file
91
library/alloc/src/ffi/mod.rs
Normal file
@ -0,0 +1,91 @@
|
||||
//! Utilities related to FFI bindings.
|
||||
//!
|
||||
//! This module provides utilities to handle data across non-Rust
|
||||
//! interfaces, like other programming languages and the underlying
|
||||
//! operating system. It is mainly of use for FFI (Foreign Function
|
||||
//! Interface) bindings and code that needs to exchange C-like strings
|
||||
//! with other languages.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! Rust represents owned strings with the [`String`] type, and
|
||||
//! borrowed slices of strings with the [`str`] primitive. Both are
|
||||
//! always in UTF-8 encoding, and may contain nul bytes in the middle,
|
||||
//! i.e., if you look at the bytes that make up the string, there may
|
||||
//! be a `\0` among them. Both `String` and `str` store their length
|
||||
//! explicitly; there are no nul terminators at the end of strings
|
||||
//! like in C.
|
||||
//!
|
||||
//! C strings are different from Rust strings:
|
||||
//!
|
||||
//! * **Encodings** - Rust strings are UTF-8, but C strings may use
|
||||
//! other encodings. If you are using a string from C, you should
|
||||
//! check its encoding explicitly, rather than just assuming that it
|
||||
//! is UTF-8 like you can do in Rust.
|
||||
//!
|
||||
//! * **Character size** - C strings may use `char` or `wchar_t`-sized
|
||||
//! characters; please **note** that C's `char` is different from Rust's.
|
||||
//! The C standard leaves the actual sizes of those types open to
|
||||
//! interpretation, but defines different APIs for strings made up of
|
||||
//! each character type. Rust strings are always UTF-8, so different
|
||||
//! Unicode characters will be encoded in a variable number of bytes
|
||||
//! each. The Rust type [`char`] represents a '[Unicode scalar
|
||||
//! value]', which is similar to, but not the same as, a '[Unicode
|
||||
//! code point]'.
|
||||
//!
|
||||
//! * **Nul terminators and implicit string lengths** - Often, C
|
||||
//! strings are nul-terminated, i.e., they have a `\0` character at the
|
||||
//! end. The length of a string buffer is not stored, but has to be
|
||||
//! calculated; to compute the length of a string, C code must
|
||||
//! manually call a function like `strlen()` for `char`-based strings,
|
||||
//! or `wcslen()` for `wchar_t`-based ones. Those functions return
|
||||
//! the number of characters in the string excluding the nul
|
||||
//! terminator, so the buffer length is really `len+1` characters.
|
||||
//! Rust strings don't have a nul terminator; their length is always
|
||||
//! stored and does not need to be calculated. While in Rust
|
||||
//! accessing a string's length is an *O*(1) operation (because the
|
||||
//! length is stored); in C it is an *O*(*n*) operation because the
|
||||
//! length needs to be computed by scanning the string for the nul
|
||||
//! terminator.
|
||||
//!
|
||||
//! * **Internal nul characters** - When C strings have a nul
|
||||
//! terminator character, this usually means that they cannot have nul
|
||||
//! characters in the middle — a nul character would essentially
|
||||
//! truncate the string. Rust strings *can* have nul characters in
|
||||
//! the middle, because nul does not have to mark the end of the
|
||||
//! string in Rust.
|
||||
//!
|
||||
//! # Representations of non-Rust strings
|
||||
//!
|
||||
//! [`CString`] and [`CStr`] are useful when you need to transfer
|
||||
//! UTF-8 strings to and from languages with a C ABI, like Python.
|
||||
//!
|
||||
//! * **From Rust to C:** [`CString`] represents an owned, C-friendly
|
||||
//! string: it is nul-terminated, and has no internal nul characters.
|
||||
//! Rust code can create a [`CString`] out of a normal string (provided
|
||||
//! that the string doesn't have nul characters in the middle), and
|
||||
//! then use a variety of methods to obtain a raw <code>\*mut [u8]</code> that can
|
||||
//! then be passed as an argument to functions which use the C
|
||||
//! conventions for strings.
|
||||
//!
|
||||
//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it
|
||||
//! is what you would use to wrap a raw <code>\*const [u8]</code> that you got from
|
||||
//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array
|
||||
//! of bytes. Once you have a [`CStr`], you can convert it to a Rust
|
||||
//! <code>&[str]</code> if it's valid UTF-8, or lossily convert it by adding
|
||||
//! replacement characters.
|
||||
//!
|
||||
//! [`String`]: crate::string::String
|
||||
//! [`CStr`]: core::ffi::CStr
|
||||
|
||||
#![unstable(feature = "alloc_ffi", issue = "94079")]
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
#[unstable(feature = "cstr_internals", issue = "none")]
|
||||
pub use self::c_str::CStrExt;
|
||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
||||
pub use self::c_str::FromVecWithNulError;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::c_str::{CString, IntoStringError, NulError};
|
||||
|
||||
mod c_str;
|
@ -91,6 +91,7 @@
|
||||
#![feature(array_chunks)]
|
||||
#![feature(array_methods)]
|
||||
#![feature(array_windows)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(async_iterator)]
|
||||
#![feature(coerce_unsized)]
|
||||
#![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))]
|
||||
@ -105,8 +106,10 @@
|
||||
#![feature(const_maybe_uninit_as_mut_ptr)]
|
||||
#![feature(const_refs_to_cell)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(core_ffi_c)]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(const_pin)]
|
||||
#![feature(cstr_from_bytes_until_nul)]
|
||||
#![feature(dispatch_from_dyn)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(extend_one)]
|
||||
@ -152,6 +155,7 @@
|
||||
#![feature(exclusive_range_pattern)]
|
||||
#![feature(fundamental)]
|
||||
#![cfg_attr(not(test), feature(generator_trait))]
|
||||
#![feature(hashmap_internals)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(let_else)]
|
||||
#![feature(min_specialization)]
|
||||
@ -160,6 +164,7 @@
|
||||
#![feature(nll)] // Not necessary, but here to test the `nll` feature.
|
||||
#![feature(rustc_allow_const_fn_unstable)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(slice_internals)]
|
||||
#![feature(staged_api)]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
#![feature(unboxed_closures)]
|
||||
@ -205,6 +210,8 @@ mod boxed {
|
||||
}
|
||||
pub mod borrow;
|
||||
pub mod collections;
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
pub mod ffi;
|
||||
pub mod fmt;
|
||||
pub mod rc;
|
||||
pub mod slice;
|
||||
|
@ -153,7 +153,7 @@
|
||||
// functions are actually methods that are in `impl [T]` but not in
|
||||
// `core::slice::SliceExt` - we need to supply these functions for the
|
||||
// `test_permutations` test
|
||||
mod hack {
|
||||
pub(crate) mod hack {
|
||||
use core::alloc::Allocator;
|
||||
|
||||
use crate::boxed::Box;
|
||||
|
18
library/alloc/tests/c_str.rs
Normal file
18
library/alloc/tests/c_str.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use std::borrow::Cow::{Borrowed, Owned};
|
||||
use std::ffi::{c_char, CStr};
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
let data = b"123\xE2\x80\xA6\0";
|
||||
let ptr = data.as_ptr() as *const c_char;
|
||||
unsafe {
|
||||
assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…"));
|
||||
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…"));
|
||||
}
|
||||
let data = b"123\xE2\0";
|
||||
let ptr = data.as_ptr() as *const c_char;
|
||||
unsafe {
|
||||
assert!(CStr::from_ptr(ptr).to_str().is_err());
|
||||
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::<str>(format!("123\u{FFFD}")));
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
#![feature(const_nonnull_slice_from_raw_parts)]
|
||||
#![feature(const_ptr_write)]
|
||||
#![feature(const_try)]
|
||||
#![feature(core_ffi_c)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(drain_filter)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
@ -49,6 +50,7 @@
|
||||
mod borrow;
|
||||
mod boxed;
|
||||
mod btree_set_hash;
|
||||
mod c_str;
|
||||
mod const_fns;
|
||||
mod cow_str;
|
||||
mod fmt;
|
||||
|
570
library/core/src/ffi/c_str.rs
Normal file
570
library/core/src/ffi/c_str.rs
Normal file
@ -0,0 +1,570 @@
|
||||
use crate::ascii;
|
||||
use crate::cmp::Ordering;
|
||||
use crate::ffi::c_char;
|
||||
use crate::fmt::{self, Write};
|
||||
use crate::ops;
|
||||
use crate::slice;
|
||||
use crate::slice::memchr;
|
||||
use crate::str;
|
||||
|
||||
/// Representation of a borrowed C string.
|
||||
///
|
||||
/// This type represents a borrowed reference to a nul-terminated
|
||||
/// array of bytes. It can be constructed safely from a <code>&[[u8]]</code>
|
||||
/// slice, or unsafely from a raw `*const c_char`. It can then be
|
||||
/// converted to a Rust <code>&[str]</code> by performing UTF-8 validation, or
|
||||
/// into an owned `CString`.
|
||||
///
|
||||
/// `&CStr` is to `CString` as <code>&[str]</code> is to `String`: the former
|
||||
/// in each pair are borrowed references; the latter are owned
|
||||
/// strings.
|
||||
///
|
||||
/// Note that this structure is **not** `repr(C)` and is not recommended to be
|
||||
/// placed in the signatures of FFI functions. Instead, safe wrappers of FFI
|
||||
/// functions may leverage the unsafe [`CStr::from_ptr`] constructor to provide
|
||||
/// a safe interface to other consumers.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Inspecting a foreign C string:
|
||||
///
|
||||
/// ```ignore (extern-declaration)
|
||||
/// use std::ffi::CStr;
|
||||
/// use std::os::raw::c_char;
|
||||
///
|
||||
/// extern "C" { fn my_string() -> *const c_char; }
|
||||
///
|
||||
/// unsafe {
|
||||
/// let slice = CStr::from_ptr(my_string());
|
||||
/// println!("string buffer size without nul terminator: {}", slice.to_bytes().len());
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Passing a Rust-originating C string:
|
||||
///
|
||||
/// ```ignore (extern-declaration)
|
||||
/// use std::ffi::{CString, CStr};
|
||||
/// use std::os::raw::c_char;
|
||||
///
|
||||
/// fn work(data: &CStr) {
|
||||
/// extern "C" { fn work_with(data: *const c_char); }
|
||||
///
|
||||
/// unsafe { work_with(data.as_ptr()) }
|
||||
/// }
|
||||
///
|
||||
/// let s = CString::new("data data data data").expect("CString::new failed");
|
||||
/// work(&s);
|
||||
/// ```
|
||||
///
|
||||
/// Converting a foreign C string into a Rust `String`:
|
||||
///
|
||||
/// ```ignore (extern-declaration)
|
||||
/// use std::ffi::CStr;
|
||||
/// use std::os::raw::c_char;
|
||||
///
|
||||
/// extern "C" { fn my_string() -> *const c_char; }
|
||||
///
|
||||
/// fn my_string_safe() -> String {
|
||||
/// unsafe {
|
||||
/// CStr::from_ptr(my_string()).to_string_lossy().into_owned()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// println!("string: {}", my_string_safe());
|
||||
/// ```
|
||||
///
|
||||
/// [str]: prim@str "str"
|
||||
#[derive(Hash)]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "CStr")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(bootstrap), lang = "CStr")]
|
||||
// FIXME:
|
||||
// `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies
|
||||
// on `CStr` being layout-compatible with `[u8]`.
|
||||
// When attribute privacy is implemented, `CStr` should be annotated as `#[repr(transparent)]`.
|
||||
// Anyway, `CStr` representation and layout are considered implementation detail, are
|
||||
// not documented and must not be relied upon.
|
||||
pub struct CStr {
|
||||
// FIXME: this should not be represented with a DST slice but rather with
|
||||
// just a raw `c_char` along with some form of marker to make
|
||||
// this an unsized type. Essentially `sizeof(&CStr)` should be the
|
||||
// same as `sizeof(&c_char)` but `CStr` should be an unsized type.
|
||||
inner: [c_char],
|
||||
}
|
||||
|
||||
/// An error indicating that a nul byte was not in the expected position.
|
||||
///
|
||||
/// The slice used to create a [`CStr`] must have one and only one nul byte,
|
||||
/// positioned at the end.
|
||||
///
|
||||
/// This error is created by the [`CStr::from_bytes_with_nul`] method.
|
||||
/// See its documentation for more.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::{CStr, FromBytesWithNulError};
|
||||
///
|
||||
/// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err();
|
||||
/// ```
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
pub struct FromBytesWithNulError {
|
||||
kind: FromBytesWithNulErrorKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
enum FromBytesWithNulErrorKind {
|
||||
InteriorNul(usize),
|
||||
NotNulTerminated,
|
||||
}
|
||||
|
||||
impl FromBytesWithNulError {
|
||||
fn interior_nul(pos: usize) -> FromBytesWithNulError {
|
||||
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) }
|
||||
}
|
||||
fn not_nul_terminated() -> FromBytesWithNulError {
|
||||
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated }
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "cstr_internals", issue = "none")]
|
||||
pub fn __description(&self) -> &str {
|
||||
match self.kind {
|
||||
FromBytesWithNulErrorKind::InteriorNul(..) => {
|
||||
"data provided contains an interior nul byte"
|
||||
}
|
||||
FromBytesWithNulErrorKind::NotNulTerminated => "data provided is not nul terminated",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error indicating that no nul byte was present.
|
||||
///
|
||||
/// A slice used to create a [`CStr`] must contain a nul byte somewhere
|
||||
/// within the slice.
|
||||
///
|
||||
/// This error is created by the [`CStr::from_bytes_until_nul`] method.
|
||||
///
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
pub struct FromBytesUntilNulError(());
|
||||
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
impl fmt::Display for FromBytesUntilNulError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "data provided does not contain a nul")
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstr_debug", since = "1.3.0")]
|
||||
impl fmt::Debug for CStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "\"")?;
|
||||
for byte in self.to_bytes().iter().flat_map(|&b| ascii::escape_default(b)) {
|
||||
f.write_char(byte as char)?;
|
||||
}
|
||||
write!(f, "\"")
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstr_default", since = "1.10.0")]
|
||||
impl Default for &CStr {
|
||||
fn default() -> Self {
|
||||
const SLICE: &[c_char] = &[0];
|
||||
// SAFETY: `SLICE` is indeed pointing to a valid nul-terminated string.
|
||||
unsafe { CStr::from_ptr(SLICE.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
|
||||
impl fmt::Display for FromBytesWithNulError {
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.__description())?;
|
||||
if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind {
|
||||
write!(f, " at byte pos {pos}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CStr {
|
||||
/// Wraps a raw C string with a safe C string wrapper.
|
||||
///
|
||||
/// This function will wrap the provided `ptr` with a `CStr` wrapper, which
|
||||
/// allows inspection and interoperation of non-owned C strings. The total
|
||||
/// size of the raw C string must be smaller than `isize::MAX` **bytes**
|
||||
/// in memory due to calling the `slice::from_raw_parts` function.
|
||||
/// This method is unsafe for a number of reasons:
|
||||
///
|
||||
/// * There is no guarantee to the validity of `ptr`.
|
||||
/// * The returned lifetime is not guaranteed to be the actual lifetime of
|
||||
/// `ptr`.
|
||||
/// * There is no guarantee that the memory pointed to by `ptr` contains a
|
||||
/// valid nul terminator byte at the end of the string.
|
||||
/// * It is not guaranteed that the memory pointed by `ptr` won't change
|
||||
/// before the `CStr` has been destroyed.
|
||||
///
|
||||
/// > **Note**: This operation is intended to be a 0-cost cast but it is
|
||||
/// > currently implemented with an up-front calculation of the length of
|
||||
/// > the string. This is not guaranteed to always be the case.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore (extern-declaration)
|
||||
/// # fn main() {
|
||||
/// use std::ffi::CStr;
|
||||
/// use std::os::raw::c_char;
|
||||
///
|
||||
/// extern "C" {
|
||||
/// fn my_string() -> *const c_char;
|
||||
/// }
|
||||
///
|
||||
/// unsafe {
|
||||
/// let slice = CStr::from_ptr(my_string());
|
||||
/// println!("string returned: {}", slice.to_str().unwrap());
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
|
||||
// SAFETY: The caller has provided a pointer that points to a valid C
|
||||
// string with a NUL terminator of size less than `isize::MAX`, whose
|
||||
// content remain valid and doesn't change for the lifetime of the
|
||||
// returned `CStr`.
|
||||
//
|
||||
// Thus computing the length is fine (a NUL byte exists), the call to
|
||||
// from_raw_parts is safe because we know the length is at most `isize::MAX`, meaning
|
||||
// the call to `from_bytes_with_nul_unchecked` is correct.
|
||||
//
|
||||
// The cast from c_char to u8 is ok because a c_char is always one byte.
|
||||
unsafe {
|
||||
extern "C" {
|
||||
/// Provided by libc or compiler_builtins.
|
||||
fn strlen(s: *const c_char) -> usize;
|
||||
}
|
||||
let len = strlen(ptr);
|
||||
let ptr = ptr as *const u8;
|
||||
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a C string wrapper from a byte slice.
|
||||
///
|
||||
/// This method will create a `CStr` from any byte slice that contains at
|
||||
/// least one nul byte. The caller does not need to know or specify where
|
||||
/// the nul byte is located.
|
||||
///
|
||||
/// If the first byte is a nul character, this method will return an
|
||||
/// empty `CStr`. If multiple nul characters are present, the `CStr` will
|
||||
/// end at the first one.
|
||||
///
|
||||
/// If the slice only has a single nul byte at the end, this method is
|
||||
/// equivalent to [`CStr::from_bytes_with_nul`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// #![feature(cstr_from_bytes_until_nul)]
|
||||
///
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let mut buffer = [0u8; 16];
|
||||
/// unsafe {
|
||||
/// // Here we might call an unsafe C function that writes a string
|
||||
/// // into the buffer.
|
||||
/// let buf_ptr = buffer.as_mut_ptr();
|
||||
/// buf_ptr.write_bytes(b'A', 8);
|
||||
/// }
|
||||
/// // Attempt to extract a C nul-terminated string from the buffer.
|
||||
/// let c_str = CStr::from_bytes_until_nul(&buffer[..]).unwrap();
|
||||
/// assert_eq!(c_str.to_str().unwrap(), "AAAAAAAA");
|
||||
/// ```
|
||||
///
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
pub fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> {
|
||||
let nul_pos = memchr::memchr(0, bytes);
|
||||
match nul_pos {
|
||||
Some(nul_pos) => {
|
||||
let subslice = &bytes[..nul_pos + 1];
|
||||
// SAFETY: We know there is a nul byte at nul_pos, so this slice
|
||||
// (ending at the nul byte) is a well-formed C string.
|
||||
Ok(unsafe { CStr::from_bytes_with_nul_unchecked(subslice) })
|
||||
}
|
||||
None => Err(FromBytesUntilNulError(())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a C string wrapper from a byte slice.
|
||||
///
|
||||
/// This function will cast the provided `bytes` to a `CStr`
|
||||
/// wrapper after ensuring that the byte slice is nul-terminated
|
||||
/// and does not contain any interior nul bytes.
|
||||
///
|
||||
/// If the nul byte may not be at the end,
|
||||
/// [`CStr::from_bytes_until_nul`] can be used instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"hello\0");
|
||||
/// assert!(cstr.is_ok());
|
||||
/// ```
|
||||
///
|
||||
/// Creating a `CStr` without a trailing nul terminator is an error:
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"hello");
|
||||
/// assert!(cstr.is_err());
|
||||
/// ```
|
||||
///
|
||||
/// Creating a `CStr` with an interior nul byte is an error:
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"he\0llo\0");
|
||||
/// assert!(cstr.is_err());
|
||||
/// ```
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {
|
||||
let nul_pos = memchr::memchr(0, bytes);
|
||||
match nul_pos {
|
||||
Some(nul_pos) if nul_pos + 1 == bytes.len() => {
|
||||
// SAFETY: We know there is only one nul byte, at the end
|
||||
// of the byte slice.
|
||||
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
|
||||
}
|
||||
Some(nul_pos) => Err(FromBytesWithNulError::interior_nul(nul_pos)),
|
||||
None => Err(FromBytesWithNulError::not_nul_terminated()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsafely creates a C string wrapper from a byte slice.
|
||||
///
|
||||
/// This function will cast the provided `bytes` to a `CStr` wrapper without
|
||||
/// performing any sanity checks. The provided slice **must** be nul-terminated
|
||||
/// and not contain any interior nul bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::{CStr, CString};
|
||||
///
|
||||
/// unsafe {
|
||||
/// let cstring = CString::new("hello").expect("CString::new failed");
|
||||
/// let cstr = CStr::from_bytes_with_nul_unchecked(cstring.to_bytes_with_nul());
|
||||
/// assert_eq!(cstr, &*cstring);
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
#[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")]
|
||||
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
|
||||
// We're in a const fn, so this is the best we can do
|
||||
debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0);
|
||||
// SAFETY: Calling an inner function with the same prerequisites.
|
||||
unsafe { Self::_from_bytes_with_nul_unchecked(bytes) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const unsafe fn _from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
|
||||
// SAFETY: Casting to CStr is safe because its internal representation
|
||||
// is a [u8] too (safe only inside std).
|
||||
// Dereferencing the obtained pointer is safe because it comes from a
|
||||
// reference. Making a reference is then safe because its lifetime
|
||||
// is bound by the lifetime of the given `bytes`.
|
||||
unsafe { &*(bytes as *const [u8] as *const CStr) }
|
||||
}
|
||||
|
||||
/// Returns the inner pointer to this C string.
|
||||
///
|
||||
/// The returned pointer will be valid for as long as `self` is, and points
|
||||
/// to a contiguous region of memory terminated with a 0 byte to represent
|
||||
/// the end of the string.
|
||||
///
|
||||
/// **WARNING**
|
||||
///
|
||||
/// The returned pointer is read-only; writing to it (including passing it
|
||||
/// to C code that writes to it) causes undefined behavior.
|
||||
///
|
||||
/// It is your responsibility to make sure that the underlying memory is not
|
||||
/// freed too early. For example, the following code will cause undefined
|
||||
/// behavior when `ptr` is used inside the `unsafe` block:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #![allow(unused_must_use)] #![allow(temporary_cstring_as_ptr)]
|
||||
/// use std::ffi::CString;
|
||||
///
|
||||
/// let ptr = CString::new("Hello").expect("CString::new failed").as_ptr();
|
||||
/// unsafe {
|
||||
/// // `ptr` is dangling
|
||||
/// *ptr;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This happens because the pointer returned by `as_ptr` does not carry any
|
||||
/// lifetime information and the `CString` is deallocated immediately after
|
||||
/// the `CString::new("Hello").expect("CString::new failed").as_ptr()`
|
||||
/// expression is evaluated.
|
||||
/// To fix the problem, bind the `CString` to a local variable:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #![allow(unused_must_use)]
|
||||
/// use std::ffi::CString;
|
||||
///
|
||||
/// let hello = CString::new("Hello").expect("CString::new failed");
|
||||
/// let ptr = hello.as_ptr();
|
||||
/// unsafe {
|
||||
/// // `ptr` is valid because `hello` is in scope
|
||||
/// *ptr;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This way, the lifetime of the `CString` in `hello` encompasses
|
||||
/// the lifetime of `ptr` and the `unsafe` block.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_str_as_ptr", since = "1.32.0")]
|
||||
pub const fn as_ptr(&self) -> *const c_char {
|
||||
self.inner.as_ptr()
|
||||
}
|
||||
|
||||
/// Converts this C string to a byte slice.
|
||||
///
|
||||
/// The returned slice will **not** contain the trailing nul terminator that this C
|
||||
/// string has.
|
||||
///
|
||||
/// > **Note**: This method is currently implemented as a constant-time
|
||||
/// > cast, but it is planned to alter its definition in the future to
|
||||
/// > perform the length calculation whenever this method is called.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
|
||||
/// assert_eq!(cstr.to_bytes(), b"foo");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn to_bytes(&self) -> &[u8] {
|
||||
let bytes = self.to_bytes_with_nul();
|
||||
// SAFETY: to_bytes_with_nul returns slice with length at least 1
|
||||
unsafe { bytes.get_unchecked(..bytes.len() - 1) }
|
||||
}
|
||||
|
||||
/// Converts this C string to a byte slice containing the trailing 0 byte.
|
||||
///
|
||||
/// This function is the equivalent of [`CStr::to_bytes`] except that it
|
||||
/// will retain the trailing nul terminator instead of chopping it off.
|
||||
///
|
||||
/// > **Note**: This method is currently implemented as a 0-cost cast, but
|
||||
/// > it is planned to alter its definition in the future to perform the
|
||||
/// > length calculation whenever this method is called.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
|
||||
/// assert_eq!(cstr.to_bytes_with_nul(), b"foo\0");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn to_bytes_with_nul(&self) -> &[u8] {
|
||||
// SAFETY: Transmuting a slice of `c_char`s to a slice of `u8`s
|
||||
// is safe on all supported targets.
|
||||
unsafe { &*(&self.inner as *const [c_char] as *const [u8]) }
|
||||
}
|
||||
|
||||
/// Yields a <code>&[str]</code> slice if the `CStr` contains valid UTF-8.
|
||||
///
|
||||
/// If the contents of the `CStr` are valid UTF-8 data, this
|
||||
/// function will return the corresponding <code>&[str]</code> slice. Otherwise,
|
||||
/// it will return an error with details of where UTF-8 validation failed.
|
||||
///
|
||||
/// [str]: prim@str "str"
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
|
||||
/// assert_eq!(cstr.to_str(), Ok("foo"));
|
||||
/// ```
|
||||
#[stable(feature = "cstr_to_str", since = "1.4.0")]
|
||||
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
|
||||
// N.B., when `CStr` is changed to perform the length check in `.to_bytes()`
|
||||
// instead of in `from_ptr()`, it may be worth considering if this should
|
||||
// be rewritten to do the UTF-8 check inline with the length calculation
|
||||
// instead of doing it afterwards.
|
||||
str::from_utf8(self.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialEq for CStr {
|
||||
fn eq(&self, other: &CStr) -> bool {
|
||||
self.to_bytes().eq(other.to_bytes())
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Eq for CStr {}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialOrd for CStr {
|
||||
fn partial_cmp(&self, other: &CStr) -> Option<Ordering> {
|
||||
self.to_bytes().partial_cmp(&other.to_bytes())
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Ord for CStr {
|
||||
fn cmp(&self, other: &CStr) -> Ordering {
|
||||
self.to_bytes().cmp(&other.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstr_range_from", since = "1.47.0")]
|
||||
impl ops::Index<ops::RangeFrom<usize>> for CStr {
|
||||
type Output = CStr;
|
||||
|
||||
fn index(&self, index: ops::RangeFrom<usize>) -> &CStr {
|
||||
let bytes = self.to_bytes_with_nul();
|
||||
// we need to manually check the starting index to account for the null
|
||||
// byte, since otherwise we could get an empty string that doesn't end
|
||||
// in a null.
|
||||
if index.start < bytes.len() {
|
||||
// SAFETY: Non-empty tail of a valid `CStr` is still a valid `CStr`.
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(&bytes[index.start..]) }
|
||||
} else {
|
||||
panic!(
|
||||
"index out of bounds: the len is {} but the index is {}",
|
||||
bytes.len(),
|
||||
index.start
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cstring_asref", since = "1.7.0")]
|
||||
impl AsRef<CStr> for CStr {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &CStr {
|
||||
self
|
||||
}
|
||||
}
|
@ -14,6 +14,11 @@
|
||||
use crate::num::*;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::c_str::{CStr, FromBytesUntilNulError, FromBytesWithNulError};
|
||||
|
||||
mod c_str;
|
||||
|
||||
macro_rules! type_alias_no_nz {
|
||||
{
|
||||
$Docfile:tt, $Alias:ident = $Real:ty;
|
||||
|
@ -20,7 +20,7 @@
|
||||
// FIXME: Fill me in with more detail when the interface settles
|
||||
//! This library is built on the assumption of a few existing symbols:
|
||||
//!
|
||||
//! * `memcpy`, `memcmp`, `memset` - These are core memory routines which are
|
||||
//! * `memcpy`, `memcmp`, `memset`, `strlen` - These are core memory routines which are
|
||||
//! often generated by LLVM. Additionally, this library can make explicit
|
||||
//! calls to these functions. Their signatures are the same as found in C.
|
||||
//! These functions are often provided by the system libc, but can also be
|
||||
|
@ -16,7 +16,7 @@ panic_unwind = { path = "../panic_unwind", optional = true }
|
||||
panic_abort = { path = "../panic_abort" }
|
||||
core = { path = "../core" }
|
||||
libc = { version = "0.2.116", default-features = false, features = ['rustc-dep-of-std'] }
|
||||
compiler_builtins = { version = "0.1.69" }
|
||||
compiler_builtins = { version = "0.1.71" }
|
||||
profiler_builtins = { path = "../profiler_builtins", optional = true }
|
||||
unwind = { path = "../unwind" }
|
||||
hashbrown = { version = "0.12", default-features = false, features = ['rustc-dep-of-std'] }
|
||||
|
@ -26,6 +26,7 @@
|
||||
use crate::cell;
|
||||
use crate::char;
|
||||
use crate::fmt::{self, Debug, Display, Write};
|
||||
use crate::io;
|
||||
use crate::mem::transmute;
|
||||
use crate::num;
|
||||
use crate::str;
|
||||
@ -612,6 +613,48 @@ impl Error for alloc::collections::TryReserveError {}
|
||||
#[unstable(feature = "duration_checked_float", issue = "83400")]
|
||||
impl Error for time::FromFloatSecsError {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Error for alloc::ffi::NulError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
"nul byte found in data"
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl From<alloc::ffi::NulError> for io::Error {
|
||||
/// Converts a [`alloc::ffi::NulError`] into a [`io::Error`].
|
||||
fn from(_: alloc::ffi::NulError) -> io::Error {
|
||||
io::const_io_error!(io::ErrorKind::InvalidInput, "data provided contains a nul byte")
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
|
||||
impl Error for core::ffi::FromBytesWithNulError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
self.__description()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
impl Error for core::ffi::FromBytesUntilNulError {}
|
||||
|
||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
||||
impl Error for alloc::ffi::FromVecWithNulError {}
|
||||
|
||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
||||
impl Error for alloc::ffi::IntoStringError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
"C string contained non-utf8 bytes"
|
||||
}
|
||||
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(self.__source())
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from `any.rs`.
|
||||
impl dyn Error + 'static {
|
||||
/// Returns `true` if the inner type is the same as `T`.
|
||||
|
@ -146,12 +146,14 @@
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
pub use self::c_str::FromBytesWithNulError;
|
||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
||||
pub use self::c_str::FromVecWithNulError;
|
||||
pub use alloc::ffi::FromVecWithNulError;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::c_str::{CStr, CString, IntoStringError, NulError};
|
||||
pub use alloc::ffi::{CString, IntoStringError, NulError};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::ffi::CStr;
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
pub use core::ffi::FromBytesWithNulError;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::os_str::{OsStr, OsString};
|
||||
@ -176,5 +178,4 @@
|
||||
)]
|
||||
pub use core::ffi::{VaList, VaListImpl};
|
||||
|
||||
mod c_str;
|
||||
mod os_str;
|
||||
|
@ -261,6 +261,8 @@
|
||||
#![feature(char_error_internals)]
|
||||
#![feature(char_internals)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(cstr_from_bytes_until_nul)]
|
||||
#![feature(cstr_internals)]
|
||||
#![feature(duration_checked_float)]
|
||||
#![feature(duration_constants)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
|
@ -95,3 +95,7 @@
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::vec::Vec;
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
#[unstable(feature = "cstr_internals", issue = "none")]
|
||||
pub use alloc::ffi::CStrExt;
|
||||
|
Loading…
Reference in New Issue
Block a user