shared_vec/
string.rs

1use crate::counter::Counter;
2use crate::Vec;
3use alloc::boxed::Box;
4use core::fmt;
5use core::ops::{Deref, RangeBounds};
6use core::str::Utf8Error;
7
8/// An immutable reference-counted string type.
9///
10/// You can borrow slices of the string without using a life-time-bound reference,
11/// and clone the string to create new references to the same data.
12///
13/// You can use [`RcString`] or [`ArcString`] as type aliases for common counter types.
14///
15/// [`RcString`]: crate::RcString
16/// [`ArcString`]: crate::ArcString
17pub struct String<C: Counter<usize>> {
18    vec: Vec<C, u8>,
19}
20
21impl<C: Counter<usize>> String<C> {
22    /// Create a new empty `String`.
23    #[inline]
24    pub fn new() -> Self {
25        Self { vec: Vec::new() }
26    }
27
28    /// Create a `String` from a `Box<[u8]>`
29    ///
30    /// # Errors
31    ///
32    /// Returns an error if the bytes are not valid UTF-8.
33    #[inline]
34    pub fn from_utf8(bytes: Box<[u8]>) -> Result<Self, Utf8Error> {
35        str::from_utf8(&bytes)?;
36        Ok(Self {
37            vec: Vec::from_boxed_slice(bytes),
38        })
39    }
40
41    /// Create a `String` from a `Box<[u8]>` without checking for UTF-8 validity.
42    ///
43    /// # Safety
44    ///
45    /// The caller must ensure that the bytes are valid UTF-8.
46    #[inline]
47    pub unsafe fn from_utf8_unchecked(bytes: Box<[u8]>) -> Self {
48        Self {
49            vec: Vec::from_boxed_slice(bytes),
50        }
51    }
52
53    /// Create a `String` from a `Box<str>`
54    #[inline]
55    pub fn from_boxed_str(bytes: Box<str>) -> Self {
56        unsafe { Self::from_utf8_unchecked(bytes.into_boxed_bytes()) }
57    }
58}
59
60impl<C: Counter<usize>> Default for String<C> {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66impl<C: Counter<usize>> Clone for String<C> {
67    /// Clones the `String`, creating a new reference to the same data.
68    #[inline]
69    fn clone(&self) -> Self {
70        Self {
71            vec: self.vec.clone(),
72        }
73    }
74}
75
76impl<C: Counter<usize>> Deref for String<C> {
77    type Target = str;
78
79    #[inline]
80    fn deref(&self) -> &Self::Target {
81        unsafe { str::from_utf8_unchecked(&self.vec) }
82    }
83}
84
85impl<C: Counter<usize>> fmt::Debug for String<C> {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        <str as fmt::Debug>::fmt(&**self, f)
88    }
89}
90
91impl<C: Counter<usize>> String<C> {
92    /// Returns the length of the string.
93    #[inline]
94    pub const fn len(&self) -> usize {
95        self.vec.len()
96    }
97
98    /// Returns true if the vector is empty.
99    #[inline]
100    pub const fn is_empty(&self) -> bool {
101        self.vec.is_empty()
102    }
103
104    /// Returns a slice of the vector.
105    #[inline]
106    pub fn bytes(&self) -> &[u8] {
107        &self.vec
108    }
109
110    /// Returns a string slice.
111    #[inline]
112    pub fn as_str(&self) -> &str {
113        self
114    }
115
116    /// Returns the original string slice.
117    #[inline]
118    pub fn as_original_str(&self) -> &str {
119        unsafe { str::from_utf8_unchecked(self.vec.as_original_slice()) }
120    }
121
122    /// Return the start and length of the range without checking.
123    #[inline]
124    pub(crate) fn convert_range_unchecked(&self, range: impl RangeBounds<usize>) -> (usize, usize) {
125        self.vec.convert_range_unchecked(range)
126    }
127
128    /// Validate the range and return the start and length.
129    #[inline]
130    pub(crate) fn validate_range(&self, range: impl RangeBounds<usize>) -> Option<(usize, usize)> {
131        let cap = self.len();
132        let start = match range.start_bound() {
133            core::ops::Bound::Included(&s) => s,
134            core::ops::Bound::Excluded(&s) => s.checked_add(1)?,
135            core::ops::Bound::Unbounded => 0,
136        };
137        let end = match range.end_bound() {
138            core::ops::Bound::Included(&e) => e.checked_add(1)?,
139            core::ops::Bound::Excluded(&e) => e,
140            core::ops::Bound::Unbounded => cap,
141        };
142        if end > cap {
143            return None;
144        }
145        let s = self.as_str();
146        if !s.is_char_boundary(start) || !s.is_char_boundary(end) {
147            return None;
148        }
149        (end.checked_sub(start)).map(|len| (start, len))
150    }
151
152    /// Check if the range is valid.
153    #[inline]
154    pub fn is_valid_range(&self, range: impl RangeBounds<usize>) -> bool {
155        self.validate_range(range).is_some()
156    }
157
158    /// Get a sub-slice of the vector.
159    #[inline]
160    pub fn get(&self, range: impl RangeBounds<usize>) -> Option<Self> {
161        let (start, len) = self.validate_range(range)?;
162        Some(Self {
163            vec: unsafe { self.vec.slice(start, len) },
164        })
165    }
166
167    /// Get a sub-slice of the vector.
168    ///
169    /// # Panics
170    ///
171    /// Panics if the range is invalid.
172    #[inline]
173    pub fn idx(&self, range: impl RangeBounds<usize>) -> Self {
174        self.get(range).expect("Invalid range")
175    }
176
177    /// Get a sub-slice of the vector without checking the range.
178    ///
179    /// # Safety
180    ///
181    /// The caller must ensure that the range is valid.
182    #[inline]
183    pub unsafe fn get_unchecked(&self, range: impl RangeBounds<usize>) -> Self {
184        let (start, len) = self.convert_range_unchecked(range);
185        let vec = self.vec.slice(start, len);
186        Self { vec }
187    }
188}
189
190impl<C: Counter<usize>> fmt::Display for String<C> {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        <str as fmt::Display>::fmt(self, f)
193    }
194}