slice_string/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![deny(rust_2018_compatibility)]
3#![deny(rust_2018_idioms)]
4#![deny(missing_docs)]
5#![deny(warnings)]
6
7//! This module implements support for a String-like structure that is backed by a slice.
8
9#[cfg(feature = "ufmt-impl")]
10mod ufmt;
11
12use core::{fmt, hash, ops, str};
13pub use tinyvec;
14use tinyvec::SliceVec; // re-export
15
16/// A UTF-8-encoded growable string backed by a `u8` slice.
17///
18/// This supports some of the API from `std::String` and dereferences
19/// to `core::str`.
20#[repr(transparent)]
21#[derive(Default)]
22pub struct SliceString<'a>(SliceVec<'a, u8>);
23
24impl<'a> SliceString<'a> {
25    /// Create a new empty `SliceString` from a mutable slice.
26    ///
27    /// The capacity of the string will be the slice length.
28    pub fn new(buf: &'a mut [u8]) -> Self {
29        // Length-0 slices are always valid UTF-8.
30        unsafe { Self::from_utf8_unchecked(buf, 0) }
31    }
32
33    /// Create a new `SliceString` from a [SliceVec].
34    ///
35    /// # Safety
36    /// The data in `SliceVec` must be valid UTF-8.
37    pub unsafe fn new_unchecked(buf: SliceVec<'a, u8>) -> Self {
38        Self(buf)
39    }
40
41    /// Create a new `SliceString` from a mutable slice.
42    ///
43    /// The data in `buf[..len]` are checked to contain valid UTF-8.
44    pub fn from_utf8(buf: &'a mut [u8], len: usize) -> Result<Self, str::Utf8Error> {
45        str::from_utf8(&buf[..len])?;
46        // UTF-8 validity of the buffer up to `len` has just been checked.
47        Ok(unsafe { Self::from_utf8_unchecked(buf, len) })
48    }
49
50    /// Create a new `SliceString` from a mutable slice.
51    ///
52    /// # Safety
53    /// The data in `buf[..len]` must be valid UTF-8.
54    pub unsafe fn from_utf8_unchecked(buf: &'a mut [u8], len: usize) -> Self {
55        Self::new_unchecked(SliceVec::from_slice_len(buf, len))
56    }
57
58    /// Return a mutable reference to the inner `SliceVec`.
59    ///
60    /// # Safety
61    /// The data in the buffer must always remain valid UTF-8.
62    pub unsafe fn as_mut_slicevec(&mut self) -> &mut SliceVec<'a, u8> {
63        &mut self.0
64    }
65
66    /// Return a reference to a UTF-8 `str`.
67    pub fn as_str(&self) -> &str {
68        // UTF-8 validity has been maintained
69        unsafe { str::from_utf8_unchecked(&self.0) }
70    }
71
72    /// Return a mutable reference to a UTF-8 `str`.
73    pub fn as_mut_str(&mut self) -> &mut str {
74        // UTF-8 validity has been maintained
75        unsafe { str::from_utf8_unchecked_mut(&mut self.0) }
76    }
77
78    /// Return the maximum number of bytes this string can contain.
79    pub fn capacity(&self) -> usize {
80        self.0.capacity()
81    }
82
83    /// Set the current string length to zero.
84    pub fn clear(&mut self) {
85        self.0.clear()
86    }
87
88    /// Set the length to be at most the given number of `u8`.
89    ///
90    /// # Panics
91    /// The new length must be at a character boundary.
92    pub fn truncate(&mut self, new_len: usize) {
93        if new_len <= self.len() {
94            assert!(self.is_char_boundary(new_len));
95        }
96        self.0.truncate(new_len);
97    }
98
99    /// Return the last `char` in the string, or `None` if empty.
100    pub fn pop(&mut self) -> Option<char> {
101        let ch = self.chars().last()?;
102        self.0.truncate(self.len() - ch.len_utf8());
103        Some(ch)
104    }
105
106    /// Append a `char` to the string.
107    ///
108    /// # Panics
109    /// The remaining space must be sufficient.
110    pub fn push(&mut self, c: char) {
111        match c.len_utf8() {
112            1 => self.0.push(c as u8),
113            _ => self.push_str(c.encode_utf8(&mut [0; 4])),
114        }
115    }
116
117    /// Append a `str` to the string.
118    ///
119    /// # Panics
120    /// The remaining space must be sufficient.
121    pub fn push_str(&mut self, string: &str) {
122        self.0.extend_from_slice(string.as_bytes())
123    }
124
125    /// Split the string and return the remainder.
126    ///
127    /// The current `SliceString` capacity and length are reduced to `at`.
128    /// A new `SliceString` is returned containing the data and buffer space starting at `at`.
129    ///
130    /// # Panics
131    /// The split location must be at a character boundary.
132    pub fn split_off(&mut self, at: usize) -> SliceString<'a> {
133        if at <= self.len() {
134            assert!(self.is_char_boundary(at));
135        }
136        let new = self.0.split_off(at);
137        // UTF-8 validity is maintained
138        unsafe { Self::new_unchecked(new) }
139    }
140}
141
142impl<'a> From<SliceString<'a>> for SliceVec<'a, u8> {
143    fn from(value: SliceString<'a>) -> Self {
144        value.0
145    }
146}
147
148impl<'a> From<SliceString<'a>> for (&'a mut [u8], usize) {
149    fn from(mut value: SliceString<'a>) -> Self {
150        let data = value.as_mut_ptr();
151        let len = value.capacity();
152        // Unfortunately there is no way to destructure SliceVec
153        let data = unsafe { core::slice::from_raw_parts_mut(data, len) };
154        (data, value.len())
155    }
156}
157
158impl<'a> TryFrom<&'a mut [u8]> for SliceString<'a> {
159    type Error = str::Utf8Error;
160
161    fn try_from(value: &'a mut [u8]) -> Result<Self, Self::Error> {
162        Self::from_utf8(value, value.len())
163    }
164}
165
166impl<'a> TryFrom<SliceVec<'a, u8>> for SliceString<'a> {
167    type Error = str::Utf8Error;
168
169    fn try_from(value: SliceVec<'a, u8>) -> Result<Self, Self::Error> {
170        str::from_utf8(&value)?;
171        // UTF-8 validity has just been checked.
172        Ok(unsafe { Self::new_unchecked(value) })
173    }
174}
175
176impl<'a> ops::Deref for SliceString<'a> {
177    type Target = str;
178
179    fn deref(&self) -> &str {
180        self.as_str()
181    }
182}
183
184impl<'a> ops::DerefMut for SliceString<'a> {
185    fn deref_mut(&mut self) -> &mut str {
186        self.as_mut_str()
187    }
188}
189
190impl<'a> AsRef<str> for SliceString<'a> {
191    fn as_ref(&self) -> &str {
192        self
193    }
194}
195
196impl<'a> AsMut<str> for SliceString<'a> {
197    fn as_mut(&mut self) -> &mut str {
198        self
199    }
200}
201
202impl<'a> AsRef<SliceVec<'a, u8>> for SliceString<'a> {
203    fn as_ref(&self) -> &SliceVec<'a, u8> {
204        &self.0
205    }
206}
207
208impl<'a> AsRef<[u8]> for SliceString<'a> {
209    fn as_ref(&self) -> &[u8] {
210        self.0.as_slice()
211    }
212}
213
214impl<'a> fmt::Write for SliceString<'a> {
215    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
216        if self.capacity() < self.len() + s.len() {
217            return Err(fmt::Error);
218        }
219        self.push_str(s);
220        Ok(())
221    }
222
223    fn write_char(&mut self, c: char) -> Result<(), fmt::Error> {
224        if self.capacity() < self.len() + c.len_utf8() {
225            return Err(fmt::Error);
226        }
227        self.push(c);
228        Ok(())
229    }
230}
231
232impl<'a> fmt::Debug for SliceString<'a> {
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        <str as fmt::Debug>::fmt(self, f)
235    }
236}
237
238impl<'a> fmt::Display for SliceString<'a> {
239    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240        <str as fmt::Display>::fmt(self, f)
241    }
242}
243
244impl<'a> hash::Hash for SliceString<'a> {
245    fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
246        <str as hash::Hash>::hash(self, hasher)
247    }
248}
249
250impl<'a> PartialEq for SliceString<'a> {
251    fn eq(&self, rhs: &SliceString<'a>) -> bool {
252        str::eq(&**self, &**rhs)
253    }
254}
255
256// SliceString<'a> == str
257impl<'a> PartialEq<str> for SliceString<'a> {
258    fn eq(&self, other: &str) -> bool {
259        str::eq(&self[..], other)
260    }
261}
262
263// SliceString<'a> == &'str
264impl<'a> PartialEq<&str> for SliceString<'a> {
265    fn eq(&self, other: &&str) -> bool {
266        str::eq(&self[..], &other[..])
267    }
268}
269
270// str == SliceString<'a>
271impl<'a> PartialEq<SliceString<'a>> for str {
272    fn eq(&self, other: &SliceString<'a>) -> bool {
273        str::eq(self, &other[..])
274    }
275}
276
277// &'str == SliceString<'a>
278impl<'a> PartialEq<SliceString<'a>> for &str {
279    fn eq(&self, other: &SliceString<'a>) -> bool {
280        str::eq(&self[..], &other[..])
281    }
282}
283
284impl<'a> Eq for SliceString<'a> {}
285
286impl<'a> PartialOrd for SliceString<'a> {
287    fn partial_cmp(&self, other: &SliceString<'a>) -> Option<core::cmp::Ordering> {
288        PartialOrd::partial_cmp(&**self, &**other)
289    }
290}
291
292impl<'a> Ord for SliceString<'a> {
293    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
294        Ord::cmp(&**self, &**other)
295    }
296}
297
298impl<'a> Extend<char> for SliceString<'a> {
299    fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
300        let iterator = iter.into_iter();
301        iterator.for_each(move |c| self.push(c));
302    }
303}
304
305impl<'a> Extend<&'a char> for SliceString<'a> {
306    fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
307        self.extend(iter.into_iter().cloned());
308    }
309}
310
311impl<'a> Extend<&'a str> for SliceString<'a> {
312    fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
313        iter.into_iter().for_each(move |s| self.push_str(s));
314    }
315}
316
317impl<'a> ops::Add<&'a str> for SliceString<'a> {
318    type Output = SliceString<'a>;
319    #[inline]
320    fn add(mut self, rhs: &str) -> Self::Output {
321        self.push_str(rhs);
322        self
323    }
324}
325
326impl<'a> ops::AddAssign<&'a str> for SliceString<'a> {
327    #[inline]
328    fn add_assign(&mut self, rhs: &'a str) {
329        self.push_str(rhs);
330    }
331}
332
333// TODO: {Index,IndexMut}<{Range,RangeInclusive,RangeFrom,RangeTo,RangeToInclusive,RangeFull}>
334
335#[cfg(test)]
336mod tests {
337    use super::*;
338    use core::fmt::Write;
339
340    #[test]
341    fn new() {
342        let mut buf = [0u8; 16];
343        let mut s = SliceString::new(&mut buf[..]);
344        assert_eq!(0, s.len());
345        assert_eq!(s.capacity(), 16);
346
347        s.push_str("Hello world!");
348        assert_eq!(s.as_str(), "Hello world!");
349
350        assert!(!s.is_empty());
351        s.clear();
352        assert_eq!(s.len(), 0);
353
354        s.push_str("foo");
355        s.truncate(2);
356        assert_eq!(s.len(), 2);
357        assert_eq!(s.as_str(), "fo");
358
359        s.push('r');
360        assert_eq!(s.as_str(), "for");
361
362        s.write_str("oooooooooooooooooooooo").unwrap_err();
363
364        let mut a = s.split_off(2);
365        assert_eq!(s.as_str(), "fo");
366        assert_eq!(a.as_str(), "r");
367
368        a.push_str("ab");
369        s.push_str("");
370        s.write_char('o').unwrap_err();
371        assert_eq!(s.capacity(), 2);
372        assert_eq!(a.capacity(), 16 - 2);
373
374        let r = unsafe { s.as_mut_slicevec() };
375        assert_eq!(r.as_ref(), "fo".as_bytes());
376
377        assert_eq!(s.pop().unwrap(), 'o');
378        assert_eq!(s.pop().unwrap(), 'f');
379    }
380
381    #[test]
382    #[should_panic]
383    fn panic_push() {
384        let mut buf = [0u8; 1];
385        let mut s = SliceString::new(&mut buf[..]);
386        s.push('f');
387        s.push('o');
388    }
389
390    #[test]
391    #[should_panic]
392    fn panic_push_str() {
393        let mut buf = [0u8; 1];
394        let mut s = SliceString::new(&mut buf[..]);
395        s.push_str("fo");
396    }
397
398    #[test]
399    fn cmp() {
400        let mut b1 = "abcd".as_bytes().to_owned();
401        let s1 = SliceString::try_from(&mut b1[..]).unwrap();
402        let mut b2 = "zzzz".as_bytes().to_owned();
403        let s2 = SliceString::try_from(&mut b2[..]).unwrap();
404        assert!(s1 < s2);
405    }
406
407    #[test]
408    fn disp() {
409        let mut b1 = "abcd".as_bytes().to_owned();
410        let s1 = SliceString::try_from(&mut b1[..]).unwrap();
411        let mut s = String::new();
412        write!(s, "{}", s1).unwrap();
413        assert_eq!("abcd", s);
414    }
415
416    #[test]
417    fn pop_uenc() {
418        let mut b = "éé".as_bytes().to_owned();
419        assert_eq!(b.len(), 2 + 3);
420        let mut s = SliceString::try_from(&mut b[..]).unwrap();
421        assert_eq!(s.len(), 2 + 3);
422        assert_eq!(s.pop().unwrap(), '\u{0301}');
423        assert_eq!(s.len(), 2 + 1);
424        assert_eq!(s.pop().unwrap(), 'e');
425        assert_eq!(s.len(), 2);
426        assert_eq!(s.pop().unwrap(), 'é');
427        assert_eq!(s.len(), 0);
428        s.push('ö');
429        s.push_str("ü");
430        assert_eq!(s.as_str(), "öü");
431    }
432
433    #[test]
434    fn write() {
435        let mut b = [0; 8];
436        let mut s = SliceString::new(&mut b[..]);
437        s.write_str("a").unwrap();
438        s.write_char('b').unwrap();
439        write!(s, "cdefgh").unwrap();
440        assert_eq!(s.len(), 8);
441        write!(s, "").unwrap();
442        write!(s, "a").unwrap_err();
443    }
444
445    #[test]
446    fn extend() {
447        let mut b = [0; 8];
448        let q = ["foo"];
449        let mut i = q.iter().copied();
450        let ii = &mut i;
451        {
452            let mut s = SliceString::new(&mut b[..]);
453            s.extend(ii);
454            println!("{}", s + "d");
455        }
456        println!("{:?}", i.next());
457        println!("{}", q[0])
458    }
459}