arc_slice/
str.rs

1use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
2use core::{
3    any::Any,
4    borrow::Borrow,
5    cmp,
6    convert::Infallible,
7    fmt,
8    hash::{Hash, Hasher},
9    mem::MaybeUninit,
10    ops::{Deref, RangeBounds},
11    str::{FromStr, Utf8Error},
12};
13
14use crate::{
15    buffer::{BorrowMetadata, Buffer, StringBuffer},
16    layout::{Compact, Layout, Plain},
17    macros::is,
18    utils::offset_len,
19    ArcBytes, ArcBytesRef,
20};
21
22#[repr(transparent)]
23pub(crate) struct StringBufWrapper<B>(pub(crate) B);
24
25impl<B: StringBuffer> Buffer<u8> for StringBufWrapper<B> {
26    fn as_slice(&self) -> &[u8] {
27        self.0.as_str().as_bytes()
28    }
29
30    fn try_into_static(self) -> Result<&'static [u8], Self>
31    where
32        Self: Sized,
33    {
34        self.0.try_into_static().map(str::as_bytes).map_err(Self)
35    }
36
37    fn try_into_vec(self) -> Result<Vec<u8>, Self>
38    where
39        Self: Sized,
40    {
41        self.0
42            .try_into_string()
43            .map(String::into_bytes)
44            .map_err(Self)
45    }
46}
47
48impl<B: BorrowMetadata> BorrowMetadata for StringBufWrapper<B> {
49    type Metadata = B::Metadata;
50
51    fn borrow_metadata(&self) -> &Self::Metadata {
52        self.0.borrow_metadata()
53    }
54}
55
56#[repr(transparent)]
57pub struct ArcStr<L: Layout = Compact>(ArcBytes<L>);
58
59impl<L: Layout> ArcStr<L> {
60    #[inline]
61    pub fn new<B: StringBuffer>(buffer: B) -> Self {
62        Self::with_metadata(buffer, ())
63    }
64
65    #[cfg(not(all(loom, test)))]
66    #[inline]
67    pub const fn new_static(s: &'static str) -> Self {
68        unsafe { Self::from_utf8_unchecked(ArcBytes::new_static(s.as_bytes())) }
69    }
70
71    #[inline]
72    pub fn with_metadata<B: StringBuffer, M: Send + Sync + 'static>(
73        buffer: B,
74        metadata: M,
75    ) -> Self {
76        let buffer = StringBufWrapper(buffer);
77        unsafe { Self::from_utf8_unchecked(ArcBytes::with_metadata(buffer, metadata)) }
78    }
79
80    /// # Safety
81    ///
82    /// Calling [`B::borrow_metadata`](BorrowMetadata::borrow_metadata) must not invalidate
83    /// the buffer slice borrow. The returned metadata must not be used to invalidate the
84    /// buffer slice.
85    #[inline]
86    pub unsafe fn with_borrowed_metadata<B: StringBuffer + BorrowMetadata>(buffer: B) -> Self {
87        let buffer = StringBufWrapper(buffer);
88        unsafe { Self::from_utf8_unchecked(ArcBytes::with_borrowed_metadata(buffer)) }
89    }
90
91    #[allow(clippy::should_implement_trait)]
92    #[inline]
93    pub fn from_str(s: &str) -> Self {
94        unsafe { Self::from_utf8_unchecked(ArcBytes::from_slice(s.as_bytes())) }
95    }
96
97    #[inline]
98    pub fn from_utf8(bytes: ArcBytes<L>) -> Result<Self, FromUtf8Error<ArcBytes<L>>> {
99        match core::str::from_utf8(bytes.as_slice()) {
100            Ok(_) => Ok(Self(bytes)),
101            Err(error) => Err(FromUtf8Error { bytes, error }),
102        }
103    }
104
105    /// # Safety
106    ///
107    /// Bytes must be valid UTF-8.
108    #[inline]
109    pub const unsafe fn from_utf8_unchecked(bytes: ArcBytes<L>) -> Self {
110        Self(bytes)
111    }
112
113    #[inline]
114    pub const fn len(&self) -> usize {
115        self.0.len()
116    }
117
118    #[inline]
119    pub const fn is_empty(&self) -> bool {
120        self.0.is_empty()
121    }
122
123    #[inline]
124    pub const fn as_str(&self) -> &str {
125        unsafe { core::str::from_utf8_unchecked(self.0.as_slice()) }
126    }
127
128    #[inline]
129    pub fn truncate(&mut self, len: usize) {
130        check_char_boundary(self, len);
131        self.0.truncate(len);
132    }
133
134    #[inline]
135    pub fn advance(&mut self, offset: usize) {
136        check_char_boundary(self, offset);
137        self.0.advance(offset);
138    }
139
140    #[inline]
141    pub fn subslice(&self, range: impl RangeBounds<usize>) -> Self {
142        let (offset, len) = offset_len(self.len(), range);
143        check_char_boundary(self, offset);
144        check_char_boundary(self, offset + len);
145        unsafe { Self::from_utf8_unchecked(self.0.subslice_impl(offset, len)) }
146    }
147
148    #[inline]
149    pub fn subslice_from_ref(&self, subset: &str) -> Self {
150        unsafe { Self::from_utf8_unchecked(self.0.subslice_from_ref(subset.as_bytes())) }
151    }
152
153    #[inline]
154    #[must_use = "consider `ArcString::truncate` if you don't need the other half"]
155    pub fn split_off(&mut self, at: usize) -> Self {
156        check_char_boundary(self, at);
157        unsafe { Self::from_utf8_unchecked(self.0.split_off(at)) }
158    }
159
160    #[inline]
161    #[must_use = "consider `ArcString::advance` if you don't need the other half"]
162    pub fn split_to(&mut self, at: usize) -> Self {
163        check_char_boundary(self, at);
164        unsafe { Self::from_utf8_unchecked(self.0.split_to(at)) }
165    }
166
167    #[inline]
168    pub fn into_string(self) -> String {
169        unsafe { String::from_utf8_unchecked(self.0.into_vec()) }
170    }
171
172    #[inline]
173    pub fn into_cow(self) -> Cow<'static, str> {
174        unsafe {
175            match self.0.into_cow() {
176                Cow::Borrowed(s) => Cow::Borrowed(core::str::from_utf8_unchecked(s)),
177                Cow::Owned(s) => Cow::Owned(String::from_utf8_unchecked(s)),
178            }
179        }
180    }
181
182    #[inline]
183    pub fn get_metadata<M: Any>(&self) -> Option<&M> {
184        self.0.get_metadata()
185    }
186
187    #[inline]
188    pub fn downcast_buffer<B: StringBuffer>(self) -> Result<B, Self> {
189        if is!(B, &'static str) {
190            let mut buffer = MaybeUninit::<B>::uninit();
191            let slice = self.0.downcast_buffer::<&'static [u8]>().map_err(Self)?;
192            let buffer_ptr = buffer.as_mut_ptr().cast::<&'static str>();
193            unsafe { buffer_ptr.write(core::str::from_utf8_unchecked(slice)) };
194            return Ok(unsafe { buffer.assume_init() });
195        }
196        if is!(B, String) {
197            let mut buffer = MaybeUninit::<B>::uninit();
198            let vec = self.0.downcast_buffer::<Vec<u8>>().map_err(Self)?;
199            let buffer_ptr = buffer.as_mut_ptr().cast::<String>();
200            unsafe { buffer_ptr.write(String::from_utf8_unchecked(vec)) };
201            return Ok(unsafe { buffer.assume_init() });
202        }
203        self.0
204            .downcast_buffer::<StringBufWrapper<B>>()
205            .map_err(Self)
206            .map(|s| s.0)
207    }
208
209    #[inline]
210    pub fn as_slice(&self) -> &ArcBytes<L> {
211        &self.0
212    }
213
214    #[inline]
215    pub fn into_slice(self) -> ArcBytes<L> {
216        self.0
217    }
218
219    #[inline]
220    pub fn with_layout<L2: Layout>(self) -> ArcStr<L2> {
221        ArcStr(self.0.with_layout())
222    }
223}
224
225impl<L: Layout> Deref for ArcStr<L> {
226    type Target = str;
227
228    #[inline]
229    fn deref(&self) -> &Self::Target {
230        self.as_str()
231    }
232}
233
234impl<L: Layout> AsRef<str> for ArcStr<L> {
235    #[inline]
236    fn as_ref(&self) -> &str {
237        self
238    }
239}
240
241impl<L: Layout> AsRef<[u8]> for ArcStr<L> {
242    #[inline]
243    fn as_ref(&self) -> &[u8] {
244        self.as_bytes()
245    }
246}
247
248impl<L: Layout> Hash for ArcStr<L> {
249    #[inline]
250    fn hash<H>(&self, state: &mut H)
251    where
252        H: Hasher,
253    {
254        self.as_str().hash(state);
255    }
256}
257
258impl<L: Layout> Borrow<str> for ArcStr<L> {
259    #[inline]
260    fn borrow(&self) -> &str {
261        self
262    }
263}
264
265impl<L: Layout> Clone for ArcStr<L> {
266    #[inline]
267    fn clone(&self) -> Self {
268        Self(self.0.clone())
269    }
270}
271
272#[cfg(not(all(loom, test)))]
273impl<L: Layout> Default for ArcStr<L> {
274    #[inline]
275    fn default() -> Self {
276        Self::new_static("")
277    }
278}
279
280impl<L: Layout> fmt::Debug for ArcStr<L> {
281    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282        (**self).fmt(f)
283    }
284}
285
286impl<L: Layout> fmt::Display for ArcStr<L> {
287    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288        (**self).fmt(f)
289    }
290}
291
292impl<L: Layout> PartialEq for ArcStr<L> {
293    fn eq(&self, other: &ArcStr<L>) -> bool {
294        self.as_str() == other.as_str()
295    }
296}
297
298impl<L: Layout> Eq for ArcStr<L> {}
299
300impl<L: Layout> PartialOrd for ArcStr<L> {
301    fn partial_cmp(&self, other: &ArcStr<L>) -> Option<cmp::Ordering> {
302        Some(self.cmp(other))
303    }
304}
305
306impl<L: Layout> Ord for ArcStr<L> {
307    fn cmp(&self, other: &ArcStr<L>) -> cmp::Ordering {
308        self.as_str().cmp(other.as_str())
309    }
310}
311
312impl<L: Layout> PartialEq<str> for ArcStr<L> {
313    fn eq(&self, other: &str) -> bool {
314        self.as_str() == other
315    }
316}
317
318impl<L: Layout> PartialEq<ArcStr<L>> for str {
319    fn eq(&self, other: &ArcStr<L>) -> bool {
320        *other == *self
321    }
322}
323
324impl<L: Layout> PartialEq<String> for ArcStr<L> {
325    fn eq(&self, other: &String) -> bool {
326        *self == other[..]
327    }
328}
329
330impl<L: Layout> PartialEq<ArcStr<L>> for String {
331    fn eq(&self, other: &ArcStr<L>) -> bool {
332        *other == *self
333    }
334}
335
336impl<L: Layout> PartialEq<ArcStr<L>> for &str {
337    fn eq(&self, other: &ArcStr<L>) -> bool {
338        *other == *self
339    }
340}
341
342impl<'a, L: Layout, O: ?Sized> PartialEq<&'a O> for ArcStr<L>
343where
344    ArcStr<L>: PartialEq<O>,
345{
346    fn eq(&self, other: &&'a O) -> bool {
347        *self == **other
348    }
349}
350
351impl From<ArcStr<Compact>> for ArcStr<Plain> {
352    fn from(value: ArcStr<Compact>) -> Self {
353        value.with_layout()
354    }
355}
356
357impl From<ArcStr<Plain>> for ArcStr<Compact> {
358    fn from(value: ArcStr<Plain>) -> Self {
359        value.with_layout()
360    }
361}
362
363macro_rules! std_impl {
364    ($($ty:ty),*) => {$(
365        impl<L: Layout> From<$ty> for ArcStr<L> {
366
367            #[inline]
368            fn from(value: $ty) -> Self {
369                Self::new(value)
370            }
371        }
372    )*};
373}
374std_impl!(&'static str, Box<str>, String, Cow<'static, str>);
375
376impl<L: Layout> From<ArcStr<L>> for String {
377    #[inline]
378    fn from(value: ArcStr<L>) -> Self {
379        value.into_string()
380    }
381}
382
383impl<L: Layout> From<ArcStr<L>> for Cow<'static, str> {
384    #[inline]
385    fn from(value: ArcStr<L>) -> Self {
386        value.into_cow()
387    }
388}
389
390impl<L: Layout> FromStr for ArcStr<L> {
391    type Err = Infallible;
392
393    #[inline]
394    fn from_str(s: &str) -> Result<Self, Self::Err> {
395        Ok(Self(ArcBytes::from_slice(s.as_bytes())))
396    }
397}
398
399impl<L: Layout> TryFrom<ArcBytes<L>> for ArcStr<L> {
400    type Error = FromUtf8Error<ArcBytes<L>>;
401
402    #[inline]
403    fn try_from(value: ArcBytes<L>) -> Result<Self, Self::Error> {
404        Self::from_utf8(value)
405    }
406}
407
408impl<L: Layout> From<ArcStr<L>> for ArcBytes<L> {
409    #[inline]
410    fn from(value: ArcStr<L>) -> Self {
411        value.into_slice()
412    }
413}
414
415#[derive(Clone, Copy)]
416pub struct ArcStrRef<'a, L: Layout = Compact>(ArcBytesRef<'a, L>);
417
418impl<L: Layout> Deref for ArcStrRef<'_, L> {
419    type Target = str;
420
421    #[inline]
422    fn deref(&self) -> &Self::Target {
423        unsafe { core::str::from_utf8_unchecked(&self.0) }
424    }
425}
426
427impl<L: Layout> fmt::Debug for ArcStrRef<'_, L> {
428    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429        fmt::Debug::fmt(&**self, f)
430    }
431}
432
433impl<L: Layout> ArcStrRef<'_, L> {
434    #[inline]
435    pub fn into_arc(self) -> ArcStr<L> {
436        unsafe { ArcStr::from_utf8_unchecked(self.0.into_arc()) }
437    }
438}
439
440pub struct FromUtf8Error<B> {
441    pub(crate) bytes: B,
442    pub(crate) error: Utf8Error,
443}
444
445impl<B> FromUtf8Error<B> {
446    pub fn as_bytes(&self) -> &B {
447        &self.bytes
448    }
449
450    pub fn into_bytes(self) -> B {
451        self.bytes
452    }
453
454    pub fn error(&self) -> Utf8Error {
455        self.error
456    }
457}
458
459impl<B: fmt::Debug> fmt::Debug for FromUtf8Error<B> {
460    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461        f.debug_struct("FromUtf8Error")
462            .field("bytes", &self.bytes)
463            .field("error", &self.error)
464            .finish()
465    }
466}
467
468impl<B> fmt::Display for FromUtf8Error<B> {
469    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470        self.error.fmt(f)
471    }
472}
473
474#[cfg(feature = "std")]
475const _: () = {
476    extern crate std;
477    impl<B: fmt::Debug> std::error::Error for FromUtf8Error<B> {}
478};
479
480pub(crate) fn check_char_boundary(s: &str, offset: usize) {
481    #[cold]
482    fn panic_not_a_char_boundary() -> ! {
483        panic!("not a char boundary")
484    }
485    if !s.is_char_boundary(offset) {
486        panic_not_a_char_boundary();
487    }
488}