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 #[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}