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:
Moulins 2023-04-05 22:46:48 +02:00 committed by Nathan Adams
parent 34f2c45441
commit e9a16ff5fe
5 changed files with 220 additions and 130 deletions

View File

@ -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`.

View File

@ -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) }
})
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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
}