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