diff --git a/wstr/src/buf.rs b/wstr/src/buf.rs index 75fa7e39a..05e569118 100644 --- a/wstr/src/buf.rs +++ b/wstr/src/buf.rs @@ -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::() == 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>) -> 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(buf: &mut Vec) { - 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(buf: &mut Vec) { - 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(&mut self, f: F) -> R where F: FnOnce(&mut Units, Vec>) -> 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`. diff --git a/wstr/src/common.rs b/wstr/src/common.rs index 0a501162e..faf983a3e 100644 --- a/wstr/src/common.rs +++ b/wstr/src/common.rs @@ -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 { /// 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>) -> &'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 { 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>(&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>(&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) } }) } diff --git a/wstr/src/lib.rs b/wstr/src/lib.rs index 994cb399a..4a1dd1d17 100644 --- a/wstr/src/lib.rs +++ b/wstr/src/lib.rs @@ -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; diff --git a/wstr/src/ops.rs b/wstr/src/ops.rs index 3fc186d47..9d9250048 100644 --- a/wstr/src/ops.rs +++ b/wstr/src/ops.rs @@ -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); } diff --git a/wstr/src/ptr.rs b/wstr/src/ptr.rs index 84fd1bbed..74495233e 100644 --- a/wstr/src/ptr.rs +++ b/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::` 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::` 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: &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(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(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::(us as *mut _), false), + Units::Wide(us) => (us as *const (), raw_len::(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) -> *mut WStr { +pub const unsafe fn slice(ptr: *const WStr, range: Range) -> *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) -> *mut WStr { + slice(ptr, range) as *mut WStr +}