wstr: cleanup and expose the `ptr` module
- move `WStr` declaration to `common`; - move `MAX_STRING_LEN` to the `WStr` type; - split `ptr` methods into `*const` and `*mut` variants; - add safe methods `WStrMetadata::new32/len32`; - add `WString::{from, into}_raw_parts`.
This commit is contained in:
parent
34f2c45441
commit
e9a16ff5fe
|
@ -7,16 +7,17 @@ use core::ops::{Deref, DerefMut};
|
|||
use core::ptr::NonNull;
|
||||
|
||||
use super::utils::{encode_raw_utf16, split_ascii_prefix, split_ascii_prefix_bytes, DecodeAvmUtf8};
|
||||
use super::{ptr, Units, WStr, MAX_STRING_LEN};
|
||||
use super::{ptr, Units, WStr, WStrMetadata};
|
||||
|
||||
/// An owned, extensible UCS2 string, analoguous to `String`.
|
||||
pub struct WString {
|
||||
data: NonNull<()>,
|
||||
meta: ptr::WStrMetadata,
|
||||
meta: WStrMetadata,
|
||||
// Always less than `WStr::MAX_LEN`
|
||||
capacity: u32,
|
||||
}
|
||||
|
||||
#[cfg(target_family = "wasm")]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const _: () = assert!(size_of::<WString>() == 12);
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
|
@ -32,7 +33,7 @@ impl WString {
|
|||
/// Creates a new empty `WString` with the given capacity and wideness.
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize, wide: bool) -> Self {
|
||||
if capacity > MAX_STRING_LEN {
|
||||
if capacity > WStr::MAX_LEN {
|
||||
super::panic_on_invalid_length(capacity);
|
||||
}
|
||||
|
||||
|
@ -51,7 +52,7 @@ impl WString {
|
|||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The length and the capacity cannot be greater than `MAX_STRING_LEN`.
|
||||
/// The length and the capacity cannot be greater than `WStr::MAX_LEN`.
|
||||
#[inline]
|
||||
pub unsafe fn from_buf_unchecked(buf: Units<Vec<u8>, Vec<u16>>) -> Self {
|
||||
// SAFETY: we take ownership of the buffer; avoid double frees
|
||||
|
@ -74,26 +75,26 @@ impl WString {
|
|||
// Tries to shrink the capacity below the maximum allowed WStr length.
|
||||
#[cold]
|
||||
fn shrink<T>(buf: &mut Vec<T>) {
|
||||
assert!(buf.capacity() > MAX_STRING_LEN);
|
||||
assert!(buf.capacity() > WStr::MAX_LEN);
|
||||
|
||||
let len = buf.len();
|
||||
if len > MAX_STRING_LEN {
|
||||
if len > WStr::MAX_LEN {
|
||||
super::panic_on_invalid_length(len);
|
||||
}
|
||||
|
||||
buf.shrink_to(MAX_STRING_LEN);
|
||||
buf.shrink_to(WStr::MAX_LEN);
|
||||
let ptr = ManuallyDrop::new(mem::take(buf)).as_mut_ptr();
|
||||
// SAFETY:
|
||||
// Per its contract, `Vec::shrink_to` reallocated the buffer to have
|
||||
// a capacity between `MAX_STRING_LEN` and `buf.capacity()`.
|
||||
// a capacity between `WStr::MAX_LEN` and `buf.capacity()`.
|
||||
unsafe {
|
||||
*buf = Vec::from_raw_parts(ptr, len, MAX_STRING_LEN);
|
||||
*buf = Vec::from_raw_parts(ptr, len, WStr::MAX_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ensure_valid_cap<T>(buf: &mut Vec<T>) {
|
||||
if buf.capacity() > MAX_STRING_LEN {
|
||||
if buf.capacity() > WStr::MAX_LEN {
|
||||
shrink(buf)
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +202,7 @@ impl WString {
|
|||
/// Converts this `WString` into a string slice.
|
||||
#[inline]
|
||||
pub fn as_wstr(&self) -> &WStr {
|
||||
let wstr = ptr::from_raw_parts(self.data.as_ptr(), self.meta);
|
||||
let wstr = ptr::from_raw_parts_mut(self.data.as_ptr(), self.meta);
|
||||
// SAFETY:`self` is immutably borrowed.
|
||||
unsafe { &*wstr }
|
||||
}
|
||||
|
@ -209,7 +210,7 @@ impl WString {
|
|||
/// Converts this `WString` into a mutable string slice.
|
||||
#[inline]
|
||||
pub fn as_wstr_mut(&mut self) -> &mut WStr {
|
||||
let wstr = ptr::from_raw_parts(self.data.as_ptr(), self.meta);
|
||||
let wstr = ptr::from_raw_parts_mut(self.data.as_ptr(), self.meta);
|
||||
// SAFETY:`self` is mutably borrowed.
|
||||
unsafe { &mut *wstr }
|
||||
}
|
||||
|
@ -249,9 +250,37 @@ impl WString {
|
|||
unsafe { ManuallyDrop::into_inner(this.steal_buf()) }
|
||||
}
|
||||
|
||||
/// Decomposes the `WString` into its raw components, leaking the contents.
|
||||
///
|
||||
/// This returns, in order:
|
||||
/// - the pointer to the actual buffer;
|
||||
/// - the length (number of codepoints) and wideness;
|
||||
/// - the total capacity, in term of codepoints (guaranteed to be less than `WStr::MAX_LEN`).
|
||||
///
|
||||
/// `Self::from_raw_parts` can then be called later to rebuild the `WString` back.
|
||||
#[inline]
|
||||
pub fn into_raw_parts(self) -> (*mut (), WStrMetadata, u32) {
|
||||
// Don't drop the contnts; they are managed by the caller now.
|
||||
let this = ManuallyDrop::new(self);
|
||||
(this.data.as_ptr(), this.meta, this.capacity)
|
||||
}
|
||||
|
||||
/// Rebuilds a `WString` from its raw components.
|
||||
///
|
||||
/// # Safety
|
||||
/// The arguments must come from a previous call to `Self::into_raw_parts`.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw_parts(ptr: *mut (), meta: WStrMetadata, capacity: u32) -> Self {
|
||||
Self {
|
||||
data: unsafe { NonNull::new_unchecked(ptr) },
|
||||
meta,
|
||||
capacity,
|
||||
}
|
||||
}
|
||||
|
||||
// Modify the raw internal buffer.
|
||||
//
|
||||
// Panics if the resulting buffer has a length greater than `MAX_STRING_LEN`.
|
||||
// Panics if the resulting buffer has a length greater than `WStr::MAX_LEN`.
|
||||
fn with_buf<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Units<Vec<u8>, Vec<u16>>) -> R,
|
||||
|
@ -314,10 +343,7 @@ impl WString {
|
|||
/// Truncates this `WString`, removing all contents.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
// SAFETY: 0 is always a valid length.
|
||||
unsafe {
|
||||
self.meta = ptr::WStrMetadata::new(0, self.meta.is_wide());
|
||||
}
|
||||
self.meta = ptr::WStrMetadata::new32(0, self.meta.is_wide());
|
||||
}
|
||||
|
||||
/// Appends a UTF-16 code unit to `self`.
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
use alloc::vec::Vec;
|
||||
use core::ops::{Bound, Index, IndexMut, Range, RangeBounds};
|
||||
|
||||
use super::{ptr, FromWStr, Pattern, WStr, WString, MAX_STRING_LEN};
|
||||
use super::{ptr, FromWStr, Pattern, WString};
|
||||
|
||||
/// A UCS2 string slice, analoguous to `&'a str`.
|
||||
#[repr(transparent)]
|
||||
pub struct WStr {
|
||||
/// See the `ptr` module for more details.
|
||||
_repr: [()],
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub(super) fn panic_on_invalid_length(len: usize) -> ! {
|
||||
|
@ -10,6 +17,7 @@ pub(super) fn panic_on_invalid_length(len: usize) -> ! {
|
|||
|
||||
/// A raw string buffer containing `u8` or `u16` code units.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub enum Units<T, U> {
|
||||
/// A buffer containing `u8` code units, interpreted as LATIN-1.
|
||||
Bytes(T),
|
||||
|
@ -68,14 +76,17 @@ units_from! {
|
|||
}
|
||||
|
||||
impl WStr {
|
||||
/// The maximum string length, equals to 2³¹-1.
|
||||
pub const MAX_LEN: usize = 0x7FFF_FFFF;
|
||||
|
||||
/// Creates a `&WStr` from a buffer containing 1 or 2-bytes code units.
|
||||
pub fn from_units<'a>(units: impl Into<Units<&'a [u8], &'a [u16]>>) -> &'a Self {
|
||||
let (ptr, len) = match units.into() {
|
||||
Units::Bytes(us) => (Units::Bytes(ptr::ptr_mut(us)), us.len()),
|
||||
Units::Wide(us) => (Units::Wide(ptr::ptr_mut(us)), us.len()),
|
||||
Units::Bytes(us) => (Units::Bytes(us as *const _), us.len()),
|
||||
Units::Wide(us) => (Units::Wide(us as *const _), us.len()),
|
||||
};
|
||||
|
||||
if len > MAX_STRING_LEN {
|
||||
if len > WStr::MAX_LEN {
|
||||
super::panic_on_invalid_length(len);
|
||||
}
|
||||
|
||||
|
@ -92,12 +103,12 @@ impl WStr {
|
|||
Units::Wide(us) => (Units::Wide(us as *mut _), us.len()),
|
||||
};
|
||||
|
||||
if len > MAX_STRING_LEN {
|
||||
if len > WStr::MAX_LEN {
|
||||
super::panic_on_invalid_length(len);
|
||||
}
|
||||
|
||||
// SAFETY: we validated the slice length above, and the mutable borrow is valid for 'a.
|
||||
unsafe { &mut *ptr::from_units(ptr) }
|
||||
unsafe { &mut *ptr::from_units_mut(ptr) }
|
||||
}
|
||||
|
||||
/// Creates an empty string.
|
||||
|
@ -117,7 +128,7 @@ impl WStr {
|
|||
pub fn units(&self) -> Units<&[u8], &[u16]> {
|
||||
// SAFETY: `self` is a valid `WStr` borrowed immutably, so we can deref. the buffers.
|
||||
unsafe {
|
||||
match ptr::units(ptr::ptr_mut(self)) {
|
||||
match ptr::units(self) {
|
||||
Units::Bytes(us) => Units::Bytes(&*us),
|
||||
Units::Wide(us) => Units::Wide(&*us),
|
||||
}
|
||||
|
@ -129,7 +140,7 @@ impl WStr {
|
|||
pub fn units_mut(&mut self) -> super::Units<&mut [u8], &mut [u16]> {
|
||||
// SAFETY: `self` is a valid `WStr` borrowed mutably, so we can mut. deref. the buffers.
|
||||
unsafe {
|
||||
match ptr::units(self) {
|
||||
match ptr::units_mut(self) {
|
||||
Units::Bytes(us) => Units::Bytes(&mut *us),
|
||||
Units::Wide(us) => Units::Wide(&mut *us),
|
||||
}
|
||||
|
@ -140,14 +151,14 @@ impl WStr {
|
|||
#[inline]
|
||||
pub fn is_wide(&self) -> bool {
|
||||
// SAFETY: `self` is a valid `WStr`.
|
||||
unsafe { ptr::metadata(ptr::ptr_mut(self)).is_wide() }
|
||||
unsafe { ptr::WStrMetadata::of(self).is_wide() }
|
||||
}
|
||||
|
||||
/// Returns the number of code units.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
// SAFETY: `self` is a valid `WStr`.
|
||||
unsafe { ptr::metadata(ptr::ptr_mut(self)).len() }
|
||||
unsafe { ptr::WStrMetadata::of(self).len() }
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` contains no code units.
|
||||
|
@ -167,7 +178,7 @@ impl WStr {
|
|||
pub fn get(&self, i: usize) -> Option<u16> {
|
||||
if i < self.len() {
|
||||
// SAFETY: `self` is a valid `WStr` and `i` is a valid index.
|
||||
Some(unsafe { ptr::read_at(ptr::ptr_mut(self), i) })
|
||||
Some(unsafe { ptr::read_at(self, i) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -179,7 +190,7 @@ impl WStr {
|
|||
/// `i` must be less than `self.len()`.
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked(&self, i: usize) -> u16 {
|
||||
ptr::read_at(ptr::ptr_mut(self), i)
|
||||
ptr::read_at(self, i)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -211,7 +222,7 @@ impl WStr {
|
|||
pub fn slice<R: RangeBounds<usize>>(&self, range: R) -> Option<&Self> {
|
||||
self.check_range(range).map(|r| {
|
||||
// SAFETY: `self` is a valid `WStr` and `r` is a valid slice range.
|
||||
unsafe { &*ptr::slice(ptr::ptr_mut(self), r) }
|
||||
unsafe { &*ptr::slice(self, r) }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -222,7 +233,7 @@ impl WStr {
|
|||
pub fn slice_mut<R: RangeBounds<usize>>(&mut self, range: R) -> Option<&mut Self> {
|
||||
self.check_range(range).map(|r| {
|
||||
// SAFETY: `self` is a valid `WStr` and `r` is a valid slice range.
|
||||
unsafe { &mut *ptr::slice(self, r) }
|
||||
unsafe { &mut *ptr::slice_mut(self, r) }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Provides UCS2 string types for usage in AVM1 and AVM2.
|
||||
//!
|
||||
//! Internally, these types are represeted by a sequence of 1-byte or 2-bytes (wide) code units,
|
||||
//! Internally, these types are represented by a sequence of 1-byte or 2-bytes (wide) code units,
|
||||
//! that may contains null bytes or unpaired surrogates.
|
||||
//!
|
||||
//! To match Flash behavior, the string length is limited to 2³¹-1 code units;
|
||||
|
@ -18,7 +18,7 @@ mod buf;
|
|||
mod ops;
|
||||
mod parse;
|
||||
mod pattern;
|
||||
mod ptr;
|
||||
pub mod ptr;
|
||||
mod tables;
|
||||
pub mod utils;
|
||||
|
||||
|
@ -26,11 +26,11 @@ pub mod utils;
|
|||
mod tests;
|
||||
|
||||
pub use buf::WString;
|
||||
pub use common::Units;
|
||||
pub use common::{Units, WStr};
|
||||
pub use ops::{CharIndices, Chars, Iter, Split, WStrToUtf8};
|
||||
pub use parse::{FromWStr, Integer};
|
||||
pub use pattern::Pattern;
|
||||
pub use ptr::{WStr, MAX_STRING_LEN};
|
||||
pub use ptr::WStrMetadata;
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use core::borrow::Borrow;
|
||||
|
|
|
@ -270,7 +270,7 @@ pub fn str_repeat(s: &WStr, count: usize) -> WString {
|
|||
}
|
||||
|
||||
let len = s.len().saturating_mul(count);
|
||||
if len > super::MAX_STRING_LEN {
|
||||
if len > WStr::MAX_LEN {
|
||||
super::panic_on_invalid_length(len);
|
||||
}
|
||||
|
||||
|
|
239
wstr/src/ptr.rs
239
wstr/src/ptr.rs
|
@ -1,49 +1,46 @@
|
|||
//! Raw pointers to `WStr` slices.
|
||||
//!
|
||||
//! # Internal representation
|
||||
//!
|
||||
//! What we actually want here is a custom DST, but they don't exist so we must cheat
|
||||
//! and abuse the slice metadata field.
|
||||
//!
|
||||
//! The data pointer points to the start of the units buffer, which is either a
|
||||
//! `[u8]` (`Units::Bytes`) or a `[u16]` (`Units::Wide`).
|
||||
//!
|
||||
//! String lengths are limited to less than `2³¹` units long, and the kind of
|
||||
//! buffer is indicated by the 32nd bit of the raw slice length:
|
||||
//! - for `Units::Bytes`, it is a zero;
|
||||
//! - for `Units::Wide`, it is a one.
|
||||
//!
|
||||
//! Note that on 64-bits targets, this leaves the high 32 bits of the length unused.
|
||||
//!
|
||||
//! # (Un)soundness
|
||||
//!
|
||||
//! Unfortunately, this scheme is technically unsound under Stacked Borrows because of provenance:
|
||||
//! when we cast a `*mut [u8]` or `*mut [u16]` to a `*mut WStr`, we lose the provenance over the
|
||||
//! original buffer as `[()]` always occupies zero bytes (which is what allows use to mess up the slice length).
|
||||
//!
|
||||
//! As such, when we access the buffer data (e.g. in `read_at` or when converting back to raw buffer references),
|
||||
//! the read is considered out-of-bounds by Stacked Borrows and causes undefined behavior.
|
||||
//!
|
||||
//! This unsoundness doesn't seem to manifest in practice though, as Rust doesn't pass through slice
|
||||
//! length information to LLVM (yet?).
|
||||
//!
|
||||
//! One observable consequence of this is that `std::mem::size_of_val::<WStr>` won't return the actual
|
||||
//! byte length of the string contents, but will instead always return 0.
|
||||
|
||||
use core::mem::transmute;
|
||||
use core::ops::Range;
|
||||
use core::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
|
||||
|
||||
use super::Units;
|
||||
use super::{Units, WStr};
|
||||
|
||||
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
|
||||
compile_error!("WStr only supports 32-bits and 64-bits targets");
|
||||
|
||||
/// The maximum string length, equals to 2³¹-1.
|
||||
pub const MAX_STRING_LEN: usize = 0x7FFF_FFFF;
|
||||
const WIDE_MASK: u32 = MAX_STRING_LEN as u32 + 1;
|
||||
|
||||
/// A UCS2 string slice, analoguous to `&'a str`.
|
||||
#[repr(transparent)]
|
||||
pub struct WStr {
|
||||
/// The internal `WStr` representation.
|
||||
///
|
||||
/// What we actually want here is a custom DST, but they don't exist so we must cheat
|
||||
/// and abuse the slice metadata field.
|
||||
///
|
||||
/// The data pointer points to the start of the units buffer, which is either a
|
||||
/// `[u8]` (`Units::Bytes`) or a `[u16]` (`Units::Wide`).
|
||||
///
|
||||
/// String lengths are limited to less than `2³¹` units long, and the kind of
|
||||
/// buffer is indicated by the 32nd bit of the raw slice length:
|
||||
/// - for `Units::Bytes`, it is a zero;
|
||||
/// - for `Units::Wide`, it is a one.
|
||||
///
|
||||
/// Note that on 64-bits targets, this leaves the high 32 bits of the length unused.
|
||||
///
|
||||
/// # (Un)soundness
|
||||
///
|
||||
/// Unfortunately, this scheme is technically unsound under Stacked Borrows because of provenance:
|
||||
/// when we cast a `*mut [u8]` or `*mut [u16]` to a `*mut WStr`, we lose the provenance over the
|
||||
/// original buffer as `[()]` always occupies zero bytes (which is what allows use to mess up the slice length).
|
||||
///
|
||||
/// As such, when we access the buffer data (e.g. in `read_at` or when converting back to raw buffer references),
|
||||
/// the read is considered out-of-bounds by Stacked Borrows and causes undefined behavior.
|
||||
///
|
||||
/// This unsoundness doesn't seem to manifest in practice though, as Rust doesn't pass through slice
|
||||
/// length information to LLVM (yet?).
|
||||
///
|
||||
/// One observable consequence of this is that `std::mem::size_of_val::<WStr>` won't return the actual
|
||||
/// byte length of the string contents, but will instead always return 0.
|
||||
_repr: [()],
|
||||
}
|
||||
const WIDE_MASK: u32 = 0x8000_0000;
|
||||
const _: () = assert!(WIDE_MASK as usize == WStr::MAX_LEN + 1);
|
||||
|
||||
/// The metadata of a `WStr` pointer. This is always 4 bytes wide, even on 64-bits targets.
|
||||
///
|
||||
|
@ -71,119 +68,164 @@ impl WStrMetadata {
|
|||
/// Assemble `WStr` metadata from its components.
|
||||
///
|
||||
/// # Safety
|
||||
/// `len` must be less than or equal to `MAX_STRING_LEN`.
|
||||
/// `len` must be less than or equal to `WStr::MAX_LEN`.
|
||||
#[inline(always)]
|
||||
pub const unsafe fn new(len: usize, is_wide: bool) -> Self {
|
||||
Self::from_usize(len | if is_wide { WIDE_MASK as usize } else { 0 })
|
||||
}
|
||||
|
||||
/// Assemble `WStr` metadata from its components.
|
||||
///
|
||||
/// Unlike `Self::new`, this is safe, but passing a `len` bigger
|
||||
/// than `WStr::MAX_LEN` will give a bogus result.
|
||||
#[inline(always)]
|
||||
pub const fn new32(len: u32, is_wide: bool) -> Self {
|
||||
Self(len | if is_wide { WIDE_MASK } else { 0 })
|
||||
}
|
||||
|
||||
/// Gets the metadata of the given pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be non-null and its metadata must be valid.
|
||||
#[inline(always)]
|
||||
pub const unsafe fn of(ptr: *const WStr) -> Self {
|
||||
Self::from_usize(raw_len(ptr as *const _ as *mut [()]))
|
||||
}
|
||||
|
||||
/// Gets the metadata of the given mutable pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be non-null and its metadata must be valid.
|
||||
#[inline(always)]
|
||||
pub const unsafe fn of_mut(ptr: *mut WStr) -> Self {
|
||||
Self::from_usize(raw_len(ptr as *mut [()]))
|
||||
}
|
||||
|
||||
/// Returns whether this metadata describes a wide `WStr`.
|
||||
#[inline(always)]
|
||||
pub const fn is_wide(self) -> bool {
|
||||
(self.0 & WIDE_MASK) != 0
|
||||
}
|
||||
|
||||
/// Returns the length of the described `WStr`. This is never greater than `MAX_STRING_LEN`.
|
||||
/// Returns the length of the described `WStr`. This is never greater than `WStr::MAX_LEN`.
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
#[inline(always)]
|
||||
pub const fn len(self) -> usize {
|
||||
(self.0 & (WIDE_MASK - 1)) as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience method to turn a `&T` into a `*mut T`.
|
||||
#[inline]
|
||||
pub(crate) fn ptr_mut<T: ?Sized>(t: &T) -> *mut T {
|
||||
t as *const T as *mut T
|
||||
/// Same as `Self::len`, but returns an `u32`.
|
||||
#[inline(always)]
|
||||
pub const fn len32(self) -> u32 {
|
||||
self.0 & (WIDE_MASK - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Replacement for unstable `<*mut [T]>::len` method.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `ptr` must point to an allocated slice of the correct type.
|
||||
#[inline]
|
||||
unsafe fn raw_len<T>(ptr: *mut [T]) -> usize {
|
||||
let fake = ptr as *mut [()];
|
||||
// SAFETY: `ptr` points to *some* allocated storage, so we
|
||||
// can read a `[()]` (which takes up 0 bytes) out of it.
|
||||
(*fake).len()
|
||||
}
|
||||
|
||||
/// Returns an untyped pointer to the raw `WStr` buffer.
|
||||
///
|
||||
/// Depending on the value of `is_wide(ptr)`, this points to a buffer of `u8`s or `u16`s.
|
||||
#[inline]
|
||||
pub fn data(ptr: *mut WStr) -> *mut () {
|
||||
ptr.cast::<()>()
|
||||
}
|
||||
|
||||
/// Returns the metadata part of a a raw `WStr` pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `ptr` must point to some allocated storage of arbitrary size.
|
||||
/// - the pointer metadata must be valid.
|
||||
#[inline]
|
||||
pub unsafe fn metadata(ptr: *mut WStr) -> WStrMetadata {
|
||||
let raw = raw_len(ptr as *mut [()]);
|
||||
WStrMetadata::from_usize(raw)
|
||||
/// `ptr` must be non-null.
|
||||
#[inline(always)]
|
||||
const unsafe fn raw_len<T>(ptr: *mut [T]) -> usize {
|
||||
core::ptr::NonNull::new_unchecked(ptr).len()
|
||||
}
|
||||
|
||||
/// Creates a `WStr` pointer from its raw parts.
|
||||
#[inline]
|
||||
pub fn from_raw_parts(data: *mut (), metadata: WStrMetadata) -> *mut WStr {
|
||||
let slice = slice_from_raw_parts(data, metadata.0 as usize);
|
||||
slice as *mut WStr
|
||||
#[inline(always)]
|
||||
pub const fn from_raw_parts(data: *const (), metadata: WStrMetadata) -> *const WStr {
|
||||
slice_from_raw_parts(data, metadata.0 as usize) as *const WStr
|
||||
}
|
||||
|
||||
/// Creates a mutable `WStr` pointer from its raw parts.
|
||||
#[inline(always)]
|
||||
pub fn from_raw_parts_mut(data: *mut (), metadata: WStrMetadata) -> *mut WStr {
|
||||
slice_from_raw_parts_mut(data, metadata.0 as usize) as *mut WStr
|
||||
}
|
||||
|
||||
/// Creates a `WStr` pointer from a raw units buffer.
|
||||
///
|
||||
/// # Safety
|
||||
/// - the buffer length must be less than or equals to `MAX_STRING_LEN`
|
||||
/// - the buffer must point to allocated storage of arbitrary size.
|
||||
/// - the buffer must be non-null.
|
||||
/// - the buffer length must be less than or equals to `WStr::MAX_LEN`.
|
||||
#[inline]
|
||||
pub unsafe fn from_units(units: Units<*mut [u8], *mut [u16]>) -> *mut WStr {
|
||||
pub const unsafe fn from_units(units: Units<*const [u8], *const [u16]>) -> *const WStr {
|
||||
let (data, len, is_wide) = match units {
|
||||
Units::Bytes(us) => (us as *mut (), raw_len(us), false),
|
||||
Units::Wide(us) => (us as *mut (), raw_len(us), true),
|
||||
Units::Bytes(us) => (us as *const (), raw_len::<u8>(us as *mut _), false),
|
||||
Units::Wide(us) => (us as *const (), raw_len::<u16>(us as *mut _), true),
|
||||
};
|
||||
|
||||
from_raw_parts(data, WStrMetadata::new(len, is_wide))
|
||||
}
|
||||
|
||||
/// Creates a `WStr` pointer from a mutable, raw units buffer.
|
||||
///
|
||||
/// # Safety
|
||||
/// - the buffer must be non-null.
|
||||
/// - the buffer length must be less than or equals to `WStr::MAX_LEN`.
|
||||
#[inline(always)]
|
||||
pub const unsafe fn from_units_mut(units: Units<*mut [u8], *mut [u16]>) -> *mut WStr {
|
||||
// SAFETY: `Units` is `repr(C)` so the transmute is sound.
|
||||
from_units(transmute(units)) as *mut WStr
|
||||
}
|
||||
|
||||
/// Gets a pointer to the buffer designated by `ptr`.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `ptr` must point to some allocated storage of arbitrary size.
|
||||
/// - `ptr` must be non-null and its metadata must be valid.
|
||||
#[inline]
|
||||
pub unsafe fn units(ptr: *mut WStr) -> Units<*mut [u8], *mut [u16]> {
|
||||
let (data, meta) = (data(ptr), metadata(ptr));
|
||||
pub const unsafe fn units(ptr: *const WStr) -> Units<*const [u8], *const [u16]> {
|
||||
let (data, meta) = (ptr as *const (), WStrMetadata::of(ptr));
|
||||
if meta.is_wide() {
|
||||
Units::Wide(slice_from_raw_parts_mut(data as *mut u16, meta.len()))
|
||||
Units::Wide(slice_from_raw_parts(data as *const u16, meta.len()))
|
||||
} else {
|
||||
Units::Bytes(slice_from_raw_parts_mut(data as *mut u8, meta.len()))
|
||||
Units::Bytes(slice_from_raw_parts(data as *const u8, meta.len()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a mutable pointer to the buffer designated by `ptr`.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `ptr` must be non-null and its metadata must be valid.
|
||||
#[inline(always)]
|
||||
pub const unsafe fn units_mut(ptr: *mut WStr) -> Units<*mut [u8], *mut [u16]> {
|
||||
// SAFETY: `Units` is `repr(C)` so the transmute is sound.
|
||||
transmute(units(ptr))
|
||||
}
|
||||
|
||||
/// Gets a pointer to the `n`th unit of this `WStr`.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `ptr` must point to a valid `WStr`;
|
||||
/// - `i` must be less than or equals to `metadata(ptr).len()`.
|
||||
#[inline]
|
||||
pub unsafe fn offset(ptr: *mut WStr, i: usize) -> Units<*mut u8, *mut u16> {
|
||||
if metadata(ptr).is_wide() {
|
||||
pub const unsafe fn offset(ptr: *const WStr, i: usize) -> Units<*const u8, *const u16> {
|
||||
if WStrMetadata::of(ptr).is_wide() {
|
||||
Units::Wide((ptr as *mut u16).add(i))
|
||||
} else {
|
||||
Units::Bytes((ptr as *mut u8).add(i))
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a mutable pointer to the `n`th unit of this `WStr`.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `ptr` must point to a valid `WStr`;
|
||||
/// - `i` must be less than or equals to `metadata(ptr).len()`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn offset_mut(ptr: *mut WStr, i: usize) -> Units<*mut u8, *mut u16> {
|
||||
// SAFETY: `Units` is `repr(C)` so the transmute is sound.
|
||||
transmute(offset(ptr, i))
|
||||
}
|
||||
|
||||
/// Dereferences the `n`th unit of this `WStr`.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `ptr` must point to a valid `WStr` for reading;
|
||||
/// - `i` must be less than `metadata(ptr).len()`.
|
||||
pub unsafe fn read_at(ptr: *mut WStr, i: usize) -> u16 {
|
||||
#[inline]
|
||||
pub const unsafe fn read_at(ptr: *const WStr, i: usize) -> u16 {
|
||||
match offset(ptr, i) {
|
||||
Units::Bytes(p) => (*p).into(),
|
||||
Units::Bytes(p) => *p as u16,
|
||||
Units::Wide(p) => *p,
|
||||
}
|
||||
}
|
||||
|
@ -195,11 +237,22 @@ pub unsafe fn read_at(ptr: *mut WStr, i: usize) -> u16 {
|
|||
/// - `range.start` must be less than or equals to `range.end`;
|
||||
/// - `range.end` must be less than or equals to `metadata(ptr).len()`.
|
||||
#[inline]
|
||||
pub unsafe fn slice(ptr: *mut WStr, range: Range<usize>) -> *mut WStr {
|
||||
pub const unsafe fn slice(ptr: *const WStr, range: Range<usize>) -> *const WStr {
|
||||
let len = range.end - range.start;
|
||||
let (data, is_wide) = match offset(ptr, range.start) {
|
||||
Units::Bytes(p) => (p as *mut (), false),
|
||||
Units::Wide(p) => (p as *mut (), true),
|
||||
Units::Bytes(p) => (p as *const (), false),
|
||||
Units::Wide(p) => (p as *const (), true),
|
||||
};
|
||||
from_raw_parts(data, WStrMetadata::new(len, is_wide))
|
||||
}
|
||||
|
||||
/// Returns a mutable pointer to a subslice of this `WStr`.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `ptr` must point to a valid `WStr`;
|
||||
/// - `range.start` must be less than or equals to `range.end`;
|
||||
/// - `range.end` must be less than or equals to `metadata(ptr).len()`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn slice_mut(ptr: *mut WStr, range: Range<usize>) -> *mut WStr {
|
||||
slice(ptr, range) as *mut WStr
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue