Skip to main content

seq_str/
seq_str.rs

1use crate::{SeqBytes, SeqBytesIter, SeqBytesIterMut};
2use alloc::{string::String, vec::Vec};
3use core::fmt;
4
5/// A sequence of &str, stored contiguously
6///
7/// This can be used as a drop-in replacement for `Vec<String>` in some cases,
8/// with better memory locality and fewer memory allocations.
9///
10/// When using `SeqBytes` instead of `Vec<String>`, the individual strings
11/// cannot be resized, but when this isn't needed there isn't much downside otherwise.
12///
13/// The container also supports "emplace"-style APIs like `in_place_writer`, which allow you to
14/// write the next element directly into the contiguous buffer with minimal overhead.
15///
16/// `SeqStr::from_display_iter` allows you to collect an iterator of `impl Display` items,
17/// formatting them directly into the contiguous buffer.
18#[derive(Clone, Default, Eq, PartialEq, Hash)]
19pub struct SeqStr {
20    inner: SeqBytes,
21}
22
23impl SeqStr {
24    /// Create a new SeqStr
25    pub fn new() -> Self {
26        Self::default()
27    }
28
29    /// Check if the sequence is empty
30    pub fn is_empty(&self) -> bool {
31        self.inner.is_empty()
32    }
33
34    /// Get the number of str in the sequence
35    pub fn len(&self) -> usize {
36        self.inner.len()
37    }
38
39    /// Reserve capacity for more str's
40    pub fn reserve(&mut self, extra: usize) {
41        self.inner.reserve(extra);
42    }
43
44    /// Shrink container to fit the current data
45    pub fn shrink_to_fit(&mut self) {
46        self.inner.shrink_to_fit();
47    }
48
49    /// Get the i'th element of the sequence in a checked manner
50    pub fn get(&self, idx: usize) -> Option<&str> {
51        self.inner
52            .get(idx)
53            .map(|b| unsafe { str::from_utf8_unchecked(b) })
54    }
55
56    /// Get the i'th element of the sequence in a checked manner
57    pub fn get_mut(&mut self, idx: usize) -> Option<&mut str> {
58        self.inner
59            .get_mut(idx)
60            .map(|b| unsafe { str::from_utf8_unchecked_mut(b) })
61    }
62
63    /// Check if the sequence contains a particular string
64    pub fn contains(&self, s: impl AsRef<str>) -> bool {
65        self.inner.contains(s.as_ref().as_bytes())
66    }
67
68    /// Push a `&str` onto the sequence
69    pub fn push(&mut self, s: impl AsRef<str>) {
70        self.inner.push(s.as_ref().as_bytes());
71    }
72
73    /// Get the last `str` of the sequence
74    pub fn last(&self) -> Option<&str> {
75        self.inner
76            .last()
77            .map(|b| unsafe { str::from_utf8_unchecked(b) })
78    }
79
80    /// Pop the last element of the container
81    /// Note that we can't return it because of lifetimes, so call [last] before popping.
82    pub fn pop(&mut self) {
83        self.inner.pop()
84    }
85
86    /// Iterate over a range of the sequence of `&str`
87    ///
88    /// This resembles [std::collections::BTreeMap::range], and is needed becuase like `BTreeMap`,
89    /// we can't implement `Deref` or `SliceIndex<Range>` and produce a slice of our contents.
90    /// See also [as_vec].
91    pub fn range(
92        &self,
93        range_bounds: impl core::ops::RangeBounds<usize>,
94    ) -> core::iter::Map<SeqBytesIter<'_>, fn(&[u8]) -> &str> {
95        fn helper(b: &[u8]) -> &str {
96            unsafe { str::from_utf8_unchecked(b) }
97        }
98
99        self.inner.range(range_bounds).map(helper)
100    }
101
102    /// Iterate over a range of the sequence of &mut [u8]
103    pub fn range_mut(
104        &mut self,
105        range_bounds: impl core::ops::RangeBounds<usize>,
106    ) -> core::iter::Map<SeqBytesIterMut<'_>, fn(&mut [u8]) -> &mut str> {
107        fn helper(b: &mut [u8]) -> &mut str {
108            unsafe { str::from_utf8_unchecked_mut(b) }
109        }
110
111        self.inner.range_mut(range_bounds).map(helper)
112    }
113
114    /// Iterate over the sequence of `&str`
115    pub fn iter(&self) -> core::iter::Map<SeqBytesIter<'_>, fn(&[u8]) -> &str> {
116        fn helper(b: &[u8]) -> &str {
117            unsafe { str::from_utf8_unchecked(b) }
118        }
119
120        self.inner.iter().map(helper)
121    }
122
123    /// Iterate over the sequence of `&mut str`
124    pub fn iter_mut(&mut self) -> core::iter::Map<SeqBytesIterMut<'_>, fn(&mut [u8]) -> &mut str> {
125        fn helper(b: &mut [u8]) -> &mut str {
126            unsafe { str::from_utf8_unchecked_mut(b) }
127        }
128
129        self.inner.iter_mut().map(helper)
130    }
131
132    /// Iterate over the sequence of `&str`, in chunks of given size.
133    /// Returns an iterator which yields one iterator for each chunk, each of which
134    /// yields `chunk_size` string values.
135    /// The last chunk may be smaller.
136    pub fn chunks(
137        &self,
138        chunk_size: usize,
139    ) -> impl Clone + ExactSizeIterator<Item = impl Clone + ExactSizeIterator<Item = &str>> {
140        fn helper(b: &[u8]) -> &str {
141            unsafe { str::from_utf8_unchecked(b) }
142        }
143
144        self.inner.chunks(chunk_size).map(|iter| iter.map(helper))
145    }
146
147    /// Truncate to at most the first n `str`
148    pub fn truncate(&mut self, new_size: usize) {
149        self.inner.truncate(new_size)
150    }
151
152    /// Resize to contain only the first n `str`, or pad up to n `str`, with empty `str` added
153    pub fn resize(&mut self, new_size: usize) {
154        self.inner.resize(new_size)
155    }
156
157    /// Retain only those `str` satisfying a predicate.
158    /// The `str` are always visited in order, similar to [std::vec::Vec::retain].
159    pub fn retain(&mut self, mut pred: impl FnMut(&str) -> bool) {
160        self.inner
161            .retain(move |b| pred(unsafe { str::from_utf8_unchecked(b) }));
162    }
163
164    /// Retain only those `str` satisfying a predicate.
165    /// The `str` are always visited in order, similar to [std::vec::Vec::retain_mut].
166    pub fn retain_mut(&mut self, mut pred: impl FnMut(&mut str) -> bool) {
167        self.inner
168            .retain_mut(move |b| pred(unsafe { str::from_utf8_unchecked_mut(b) }));
169    }
170
171    /// Get an `impl std::fmt::Write` which can be used to write the next slice
172    /// directly into the buffer without copying.
173    pub fn in_place_writer(&mut self) -> impl core::fmt::Write {
174        // Wrapper for return
175        struct Sink<T: FnMut(&[u8])>(pub T);
176
177        impl<T: FnMut(&[u8])> core::fmt::Write for Sink<T> {
178            fn write_str(&mut self, s: &str) -> std::fmt::Result {
179                (self.0)(s.as_bytes());
180                Ok(())
181            }
182        }
183
184        Sink(self.inner.in_place_writer_no_std())
185    }
186
187    /// Construct `SeqStr` from any items that implement `Display`,
188    /// formatting them directly into the contiguous buffer.
189    pub fn from_display_iter<T: core::fmt::Display, I: Iterator<Item = T>>(it: I) -> Self {
190        use core::fmt::Write;
191
192        let mut result = Self::default();
193        result.reserve(it.size_hint().0);
194        for item in it {
195            // Note: This error is okay to unwrap, because our destination buffer is unlimited size.
196            // This is the same as what that `ToString::to_string` does with these errors in the stdlib.
197            write!(result.in_place_writer(), "{item}")
198                .expect("a Display implementation returned an error unexpectedly");
199        }
200        result
201    }
202
203    /// Express as a Vec<&str>. The main reason that this may be useful is that there are
204    /// useful methods on slice types `&[&str]`, for example, [core::slice::binary_search],
205    /// but `SeqStr` itself doesn't implement `Deref` the way that `Vec` does and can
206    /// only produce such a slice by allocating.
207    ///
208    /// See [SeqBytes::as_vec] for more discussion of tradeoffs.
209    pub fn as_vec(&self) -> Vec<&str> {
210        self.iter().collect()
211    }
212
213    /// Concatenate the `str` in the sequence into one string
214    pub fn concat(&self) -> &str {
215        unsafe { core::str::from_utf8_unchecked(self.inner.concat()) }
216    }
217
218    /// Join the `str` in the sequence into one string, placing a separator between them
219    pub fn join(&self, separator: &str) -> String {
220        let mut result = String::with_capacity(
221            self.inner.num_bytes() + self.inner.len().saturating_sub(1) * separator.len(),
222        );
223        let mut first = true;
224
225        for s in self.iter() {
226            if first {
227                first = false;
228            } else {
229                result += separator;
230            }
231            result += s;
232        }
233        result
234    }
235}
236
237impl core::ops::Index<usize> for SeqStr {
238    type Output = str;
239
240    fn index(&self, index: usize) -> &str {
241        unsafe { str::from_utf8_unchecked(self.inner.index(index)) }
242    }
243}
244
245impl core::ops::IndexMut<usize> for SeqStr {
246    fn index_mut(&mut self, index: usize) -> &mut str {
247        unsafe { str::from_utf8_unchecked_mut(self.inner.index_mut(index)) }
248    }
249}
250
251impl fmt::Debug for SeqStr {
252    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
253        f.debug_list().entries(self.iter()).finish()
254    }
255}
256
257impl<A: AsRef<str>> Extend<A> for SeqStr {
258    fn extend<T>(&mut self, iter: T)
259    where
260        T: IntoIterator<Item = A>,
261    {
262        let iter = iter.into_iter();
263        self.reserve(iter.size_hint().0);
264        for item in iter {
265            self.push(item);
266        }
267    }
268}
269
270impl<A: AsRef<str>> FromIterator<A> for SeqStr {
271    // Required method
272    fn from_iter<T>(iter: T) -> Self
273    where
274        T: IntoIterator<Item = A>,
275    {
276        let mut result = SeqStr::default();
277        result.extend(iter);
278        result
279    }
280}
281
282// IntoIterator can only be implemented for &'a SeqStr,
283// otherwise the buffer doesn't live long enough.
284impl<'a> IntoIterator for &'a SeqStr {
285    type Item = &'a str;
286    type IntoIter = core::iter::Map<SeqBytesIter<'a>, fn(&[u8]) -> &str>;
287
288    fn into_iter(self) -> Self::IntoIter {
289        fn helper(b: &[u8]) -> &str {
290            unsafe { core::str::from_utf8_unchecked(b) }
291        }
292
293        self.inner.iter().map(helper)
294    }
295}
296
297#[cfg(feature = "serde")]
298mod serde_impls {
299    use super::*;
300    use ::serde::{
301        Deserialize, Serialize,
302        de::{Deserializer, SeqAccess, Visitor},
303        ser::{SerializeSeq, Serializer},
304    };
305
306    impl Serialize for SeqStr {
307        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
308        where
309            S: Serializer,
310        {
311            let mut seq = serializer.serialize_seq(Some(self.len()))?;
312            for e in self {
313                seq.serialize_element(e)?;
314            }
315            seq.end()
316        }
317    }
318
319    impl<'de> Deserialize<'de> for SeqStr {
320        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
321        where
322            D: Deserializer<'de>,
323        {
324            deserializer.deserialize_seq(SeqStrVis {})
325        }
326    }
327
328    struct SeqStrVis {}
329
330    impl<'de> Visitor<'de> for SeqStrVis {
331        type Value = SeqStr;
332
333        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
334            formatter.write_str("a sequence of strings")
335        }
336
337        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
338        where
339            A: SeqAccess<'de>,
340        {
341            let mut result = SeqStr::default();
342
343            if let Some(size) = seq.size_hint() {
344                result.reserve(size);
345            }
346
347            loop {
348                let maybe_next_str: Option<&str> = seq.next_element()?;
349                if let Some(next_str) = maybe_next_str {
350                    result.push(next_str);
351                } else {
352                    return Ok(result);
353                }
354            }
355        }
356    }
357}
358
359#[cfg(test)]
360mod tests {
361    use super::*;
362    use alloc::{borrow::ToOwned, vec, vec::Vec};
363
364    #[test]
365    fn vec_str_conversions() {
366        let vec_str = vec!["1", "2", "3"];
367
368        let seq_str: SeqStr = vec_str.into_iter().collect();
369
370        assert_eq!(seq_str.len(), 3);
371        assert_eq!(&seq_str[0], "1");
372        assert_eq!(&seq_str[1], "2");
373        assert_eq!(&seq_str[2], "3");
374
375        let vec_str2 = seq_str.iter().map(ToOwned::to_owned).collect::<Vec<_>>();
376
377        assert_eq!(vec_str2.len(), 3);
378        assert_eq!(vec_str2[0], "1");
379        assert_eq!(vec_str2[1], "2");
380        assert_eq!(vec_str2[2], "3");
381    }
382
383    #[test]
384    fn vec_string_conversions() {
385        let vec_string = vec!["1".to_owned(), "2".to_owned(), "3".to_owned()];
386
387        let seq_str: SeqStr = vec_string.into_iter().collect();
388
389        assert_eq!(seq_str.len(), 3);
390        assert_eq!(&seq_str[0], "1");
391        assert_eq!(&seq_str[1], "2");
392        assert_eq!(&seq_str[2], "3");
393
394        let vec_str2 = seq_str.iter().map(ToOwned::to_owned).collect::<Vec<_>>();
395
396        assert_eq!(vec_str2.len(), 3);
397        assert_eq!(vec_str2[0], "1");
398        assert_eq!(vec_str2[1], "2");
399        assert_eq!(vec_str2[2], "3");
400    }
401
402    #[test]
403    fn from_display_iter() {
404        let v = vec![1, 2, 3, 45, 67];
405
406        let seq_str = SeqStr::from_display_iter(v.into_iter());
407
408        assert_eq!(seq_str.len(), 5);
409        assert_eq!(&seq_str[0], "1");
410        assert_eq!(&seq_str[1], "2");
411        assert_eq!(&seq_str[2], "3");
412        assert_eq!(&seq_str[3], "45");
413        assert_eq!(&seq_str[4], "67");
414    }
415
416    #[test]
417    fn range() {
418        let v = vec![1, 2, 3, 45, 67];
419
420        let seq_str = SeqStr::from_display_iter(v.into_iter());
421
422        let seq_str2: SeqStr = seq_str.range(1..4).collect();
423        assert_eq!(seq_str2.len(), 3);
424        assert_eq!(&seq_str2[0], "2");
425        assert_eq!(&seq_str2[1], "3");
426        assert_eq!(&seq_str2[2], "45");
427
428        let seq_str2: SeqStr = seq_str.range(3..).collect();
429        assert_eq!(seq_str2.len(), 2);
430        assert_eq!(&seq_str2[0], "45");
431        assert_eq!(&seq_str2[1], "67");
432
433        let seq_str2: SeqStr = seq_str.range(..3).collect();
434        assert_eq!(seq_str2.len(), 3);
435        assert_eq!(&seq_str2[0], "1");
436        assert_eq!(&seq_str2[1], "2");
437        assert_eq!(&seq_str2[2], "3");
438
439        let seq_str2: SeqStr = seq_str.range(..).collect();
440        assert_eq!(seq_str2.len(), 5);
441        assert_eq!(&seq_str2[0], "1");
442        assert_eq!(&seq_str2[1], "2");
443        assert_eq!(&seq_str2[2], "3");
444        assert_eq!(&seq_str2[3], "45");
445        assert_eq!(&seq_str2[4], "67");
446    }
447
448    #[test]
449    fn iter_mut() {
450        let mut seq_str = SeqStr::from_iter(["asdf", "jkl;", ""]);
451
452        assert_eq!(seq_str.len(), 3);
453        assert_eq!(&seq_str[0], "asdf");
454        assert_eq!(&seq_str[1], "jkl;");
455        assert_eq!(&seq_str[2], "");
456
457        for s in seq_str.iter_mut() {
458            s.make_ascii_uppercase();
459        }
460
461        assert_eq!(seq_str.len(), 3);
462        assert_eq!(&seq_str[0], "ASDF");
463        assert_eq!(&seq_str[1], "JKL;");
464        assert_eq!(&seq_str[2], "");
465    }
466
467    #[test]
468    fn test_serde() {
469        let seq_str: SeqStr = serde_json::from_str("[\"asdf\", \"jkl;\", \"\"]").unwrap();
470
471        assert_eq!(seq_str.len(), 3);
472        assert_eq!(&seq_str[0], "asdf");
473        assert_eq!(&seq_str[1], "jkl;");
474        assert_eq!(&seq_str[2], "");
475
476        let ser = serde_json::to_string(&seq_str).unwrap();
477
478        let seq_str2: SeqStr = serde_json::from_str(&ser).unwrap();
479
480        assert_eq!(seq_str, seq_str2);
481    }
482
483    #[test]
484    fn test_join() {
485        let seq_str = SeqStr::from_iter(["asdf", "jkl;", "", ":)"]);
486
487        assert_eq!(seq_str.concat(), "asdfjkl;:)");
488        assert_eq!(seq_str.join(", "), "asdf, jkl;, , :)");
489    }
490}