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