blob: e4778719274fedede388c0d107010c3addc415ec [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
use crate::slice_ptr::get_raw_parts;
use crate::std::string_view;
use core::ptr;
impl string_view {
/// Returns an equivalent Rust slice pointer.
///
/// The resulting slice pointer is valid for the lifetime of the pointed-to
/// object.
///
/// Note: For empty strings, the address of the slice pointer may not be the
/// same as the address of the string_view. Null pointers are converted
/// to valid, but dangling, pointers.
#[inline(always)]
pub fn as_raw_bytes(self) -> *const [u8] {
self.into()
}
}
/// Equivalent to `as_raw_bytes()`.
impl From<string_view> for *const [u8] {
fn from(sv: string_view) -> Self {
// SAFETY: `&sv` is a valid pointer. `data()` does not dereference the
// string_view.
let mut data = unsafe { string_view::data(&sv) } as *const u8;
// TODO(b/249376862): use size(), which does not have the soundness issue below.
// let size = unsafe {string_view::size(&sv)};
// SAFETY: `&sv` is a valid pointer, but the call to end() requires that the
// string_view not be dangling, so this is unsound.
// (If `sv` is dangling, then `end()` will perform pointer arithmetic on a
// dangling pointer, which is implementation defined (and treated on
// Clang as if it were UB).)
let size =
unsafe { string_view::end(&sv) } as usize - unsafe { string_view::begin(&sv) } as usize;
// Unlike C++, Rust does not allow for null data pointers in slices.
if data.is_null() {
data = ptr::NonNull::dangling().as_ptr();
debug_assert_eq!(size, 0);
}
ptr::slice_from_raw_parts(data, size)
}
}
impl From<&[u8]> for string_view {
fn from(s: &[u8]) -> Self {
string_view::from(s as *const [u8])
}
}
impl From<&str> for string_view {
fn from(s: &str) -> Self {
string_view::from(s.as_bytes())
}
}
impl From<&core::ffi::CStr> for string_view {
fn from(cstr: &core::ffi::CStr) -> Self {
string_view::from(cstr.to_bytes())
}
}
impl From<*const [u8]> for string_view {
fn from(slice: *const [u8]) -> Self {
// TODO(jeanpierreda): We can't access the constructors at the moment.
// This little maneuver's gonna cost us 51 years of annoying build breakages
// later, so really we should try to get the constructors callable.
// SAFETY: string_view (in Rust) is a `repr(C)` struct entirely composed of
// arrays of MaybeUninit<u8>, so this would be safe even without the
// zeroed(). With the zeroed, it's also safe even if we accidentally use
// the type without further initialization. (In C++, the fields are a
// pointer and an integer).
let mut sv = unsafe { <core::mem::MaybeUninit<string_view>>::zeroed().assume_init() };
// We could also use the (unstable) to_raw_parts, but that feature may change
// over time. It's also difficult, for idiosyncratic reasons, to pipe in
// the feature flag to the automatically generated bindings for
// `string_view` that this file attaches onto, or to inject a dependency
// on a crate to put this logic into. (The crate this file is a part of is
// automatically generated by Crubit, and so we would need to tell
// Crubit to add these to the generated bindings for `std`.) So for now,
// the most expedient thing, and the thing least likely to break in a
// future version of Rust, to roll our own version of to_raw_parts, which uses
// transmute under the hood.
let (ptr, size) = get_raw_parts::<u8>(slice);
// SAFETY: there is no immediate UB, because we are converting to an array of
// MaybeUninit. There is also no later UB when read by C++, where it is
// interpreted as a pointer: any non-dangling valid Rust pointer is also
// a valid C++ pointer. The only time a dangling pointer exists is if
// size == 0, in which case we replace it with null, which is
// also a valid C++ pointer.
sv.__data_ = unsafe { core::mem::transmute(if size == 0 { 0 as *const _ } else { ptr }) };
// SAFETY: there is no immediate UB, because we are converting to an array of
// MaybeUninit. There is also no later UB when read by C++, where it is
// interpreted as a `size_t`, which has the same set of object
// representations as `size : usize`.
sv.__size_ = unsafe { core::mem::transmute(size) };
sv
}
}