Skip to main content

str_stack/
lib.rs

1//! A string allocation library. This is primarily useful when you want to allocate a bunch of
2//! small strings, use them, and then destroy them all together.
3//!
4//! ## Example
5//!
6//! ```
7//! use str_stack::StrStack;
8//!
9//! let mut stack = StrStack::new();
10//! let first = stack.push("one");
11//! let second = stack.push("two");
12//! let third = stack.push("three");
13//!
14//! assert_eq!(&stack[first], "one");
15//! assert_eq!(&stack[second], "two");
16//! assert_eq!(&stack[third], "three");
17//! ```
18//!
19use std::fmt::{self, Write};
20use std::io;
21use std::iter::FromIterator;
22use std::ops::Index;
23use std::slice;
24
25#[derive(Clone)]
26pub struct StrStack {
27    data: String,
28    ends: Vec<usize>,
29}
30
31impl Default for StrStack {
32    fn default() -> Self {
33        Self::new()
34    }
35}
36
37impl Index<usize> for StrStack {
38    type Output = str;
39    #[inline]
40    fn index(&self, index: usize) -> &str {
41        unsafe {
42            assert!(index < self.len(), "index out of bounds");
43            self.get_unchecked(index)
44        }
45    }
46}
47
48#[derive(Clone)]
49pub struct Iter<'a> {
50    data: &'a str,
51    ends: &'a [usize],
52}
53
54impl fmt::Debug for StrStack {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        f.debug_list().entries(self.iter()).finish()
57    }
58}
59
60impl<'a> Iterator for Iter<'a> {
61    type Item = &'a str;
62    #[inline]
63    fn next(&mut self) -> Option<&'a str> {
64        unsafe {
65            let len = self.ends.len();
66            if len == 1 {
67                None
68            } else {
69                let start = *self.ends.get_unchecked(0);
70                let end = *self.ends.get_unchecked(1);
71                self.ends = slice::from_raw_parts(self.ends.as_ptr().offset(1), len - 1);
72                Some(self.data.get_unchecked(start..end))
73            }
74        }
75    }
76
77    fn count(self) -> usize {
78        self.size_hint().0
79    }
80
81    fn last(mut self) -> Option<&'a str> {
82        self.next_back()
83    }
84
85    #[inline]
86    fn size_hint(&self) -> (usize, Option<usize>) {
87        let len = self.ends.len() - 1;
88        (len, Some(len))
89    }
90}
91
92impl<'a> ExactSizeIterator for Iter<'a> {}
93
94impl<'a> DoubleEndedIterator for Iter<'a> {
95    #[inline]
96    fn next_back(&mut self) -> Option<&'a str> {
97        unsafe {
98            let len = self.ends.len();
99            if len == 1 {
100                None
101            } else {
102                let start = *self.ends.get_unchecked(len - 2);
103                let end = *self.ends.get_unchecked(len - 1);
104                self.ends = slice::from_raw_parts(self.ends.as_ptr(), len - 1);
105                Some(self.data.get_unchecked(start..end))
106            }
107        }
108    }
109}
110
111impl<'a> IntoIterator for &'a StrStack {
112    type IntoIter = Iter<'a>;
113    type Item = &'a str;
114    #[inline]
115    fn into_iter(self) -> Iter<'a> {
116        self.iter()
117    }
118}
119
120impl StrStack {
121    /// Create a new StrStack.
122    #[inline]
123    pub fn new() -> StrStack {
124        StrStack::with_capacity(0, 0)
125    }
126
127    /// Create a new StrStack with the given capacity.
128    ///
129    /// You will be able to push `bytes` bytes and create `strings` strings before reallocating.
130    #[inline]
131    pub fn with_capacity(bytes: usize, strings: usize) -> StrStack {
132        let mut stack = StrStack {
133            data: String::with_capacity(bytes),
134            ends: Vec::with_capacity(strings + 1),
135        };
136        // Yes, I know I don't need this. However, putting this here avoids checks later which
137        // makes this much faster.
138        stack.ends.push(0);
139        stack
140    }
141
142    /// Push a string onto the string stack.
143    ///
144    /// This returns the index of the string on the stack.
145    #[inline]
146    pub fn push(&mut self, s: &str) -> usize {
147        self.data.push_str(s);
148        self.ends.push(self.data.len());
149        self.len() - 1
150    }
151
152    /// Iterate over the strings on the stack.
153    #[inline]
154    pub fn iter(&self) -> Iter<'_> {
155        Iter {
156            data: &self.data,
157            ends: &self.ends,
158        }
159    }
160
161    /// Remove the top string from the stack.
162    ///
163    /// Returns true iff a string was removed.
164    #[inline]
165    pub fn pop(&mut self) -> bool {
166        if self.ends.len() <= 1 {
167            false
168        } else {
169            self.ends.pop();
170            self.data.truncate(*self.ends.last().unwrap());
171            true
172        }
173    }
174
175    /// Clear the stack.
176    #[inline]
177    pub fn clear(&mut self) {
178        self.ends.truncate(1);
179        self.data.clear();
180    }
181
182    /// Returns the number of strings on the stack.
183    #[inline]
184    pub fn len(&self) -> usize {
185        self.ends.len() - 1
186    }
187
188    /// Returns true if there are no strings on the stack.
189    #[inline]
190    pub fn is_empty(&self) -> bool {
191        self.ends.len() == 1
192    }
193
194    /// Truncate the stack to `len` strings.
195    #[inline]
196    pub fn truncate(&mut self, len: usize) {
197        self.ends.truncate(len.saturating_add(1));
198        self.data.truncate(*self.ends.last().unwrap());
199    }
200
201    /// Read from `source` into the string stack.
202    ///
203    /// Returns the index of the new string or an IO Error.
204    pub fn consume<R: io::Read>(&mut self, mut source: R) -> io::Result<usize> {
205        match source.read_to_string(&mut self.data) {
206            Ok(_) => {
207                self.ends.push(self.data.len());
208                Ok(self.len() - 1)
209            }
210            Err(e) => Err(e),
211        }
212    }
213
214    /// Returns a writer helper for this string stack.
215    ///
216    /// This is useful for building a string in-place on the string-stack.
217    ///
218    /// Example:
219    ///
220    /// ```
221    /// use std::fmt::Write;
222    /// use str_stack::StrStack;
223    ///
224    /// let mut s = StrStack::new();
225    /// let index = {
226    ///     let mut writer = s.writer();
227    ///     writer.write_str("Hello");
228    ///     writer.write_char(' ');
229    ///     writer.write_str("World");
230    ///     writer.write_char('!');
231    ///     writer.finish()
232    /// };
233    /// assert_eq!(&s[index], "Hello World!");
234    /// ```
235    #[inline]
236    pub fn writer(&mut self) -> Writer<'_> {
237        Writer(self)
238    }
239
240    /// Allows calling the write! macro directly on the string stack:
241    ///
242    /// Example:
243    ///
244    /// ```
245    /// use std::fmt::Write;
246    /// use str_stack::StrStack;
247    ///
248    /// let mut s = StrStack::new();
249    /// let index = write!(&mut s, "Hello {}!", "World");
250    /// assert_eq!(&s[index], "Hello World!");
251    /// ```
252    #[inline]
253    pub fn write_fmt(&mut self, args: fmt::Arguments) -> usize {
254        let mut writer = self.writer();
255        let _ = writer.write_fmt(args);
256        writer.finish()
257    }
258
259    /// Returns a reference to the string at the given index, without performing bounds checking.
260    ///
261    /// # Safety
262    ///
263    /// The caller must ensure that `index < self.len()`.
264    ///
265    /// # Example
266    ///
267    /// ```
268    /// use str_stack::StrStack;
269    ///
270    /// let mut stack = StrStack::new();
271    /// stack.push("one");
272    /// stack.push("two");
273    ///
274    /// unsafe {
275    ///     assert_eq!(stack.get_unchecked(0), "one");
276    ///     assert_eq!(stack.get_unchecked(1), "two");
277    /// }
278    /// ```
279    #[inline]
280    pub unsafe fn get_unchecked(&self, index: usize) -> &str {
281        let start = *self.ends.get_unchecked(index);
282        let end = *self.ends.get_unchecked(index + 1);
283        self.data.get_unchecked(start..end)
284    }
285}
286
287impl<S> Extend<S> for StrStack
288where
289    S: AsRef<str>,
290{
291    fn extend<T>(&mut self, iterator: T)
292    where
293        T: IntoIterator<Item = S>,
294    {
295        let iterator = iterator.into_iter();
296        let (min, _) = iterator.size_hint();
297        self.ends.reserve(min);
298        for v in iterator {
299            self.push(v.as_ref());
300        }
301    }
302}
303
304impl<S> FromIterator<S> for StrStack
305where
306    S: AsRef<str>,
307{
308    fn from_iter<T>(iterator: T) -> Self
309    where
310        T: IntoIterator<Item = S>,
311    {
312        let mut stack = StrStack::new();
313        stack.extend(iterator);
314        stack
315    }
316}
317
318pub struct Writer<'a>(&'a mut StrStack);
319
320impl<'a> Writer<'a> {
321    /// Finish pushing the string onto the stack and return its index.
322    #[inline]
323    pub fn finish(self) -> usize {
324        // We push on drop.
325        self.0.len()
326    }
327}
328
329impl<'a> fmt::Write for Writer<'a> {
330    #[inline]
331    fn write_str(&mut self, s: &str) -> fmt::Result {
332        self.0.data.push_str(s);
333        Ok(())
334    }
335    #[inline]
336    fn write_char(&mut self, c: char) -> fmt::Result {
337        self.0.data.push(c);
338        Ok(())
339    }
340}
341
342impl<'a> Drop for Writer<'a> {
343    fn drop(&mut self) {
344        self.0.ends.push(self.0.data.len());
345    }
346}
347
348#[test]
349fn test_basic() {
350    for mut stack in [StrStack::new(), StrStack::default()] {
351        let first = stack.push("one");
352        let second = stack.push("two");
353        let third = stack.push("three");
354
355        assert_eq!(&stack[first], "one");
356        assert_eq!(&stack[second], "two");
357        assert_eq!(&stack[third], "three");
358
359        assert_eq!(stack.len(), 3);
360
361        assert!(stack.pop());
362
363        assert_eq!(stack.len(), 2);
364
365        assert_eq!(&stack[first], "one");
366        assert_eq!(&stack[second], "two");
367
368        assert!(stack.pop());
369        assert!(stack.pop());
370
371        assert_eq!(stack.len(), 0);
372        assert!(!stack.pop());
373    }
374}
375
376#[test]
377fn test_consume() {
378    let mut stack = StrStack::new();
379    let idx = stack.consume("testing".as_bytes()).unwrap();
380    assert_eq!(&stack[idx], "testing");
381}
382
383#[test]
384fn test_writer() {
385    let mut stack = StrStack::new();
386    let first = {
387        let mut w = stack.writer();
388        write!(w, "{}", "first ").unwrap();
389        write!(w, "{}", "second").unwrap();
390        w.finish()
391    };
392
393    let second = {
394        let mut w = stack.writer();
395        write!(w, "{}", "third ").unwrap();
396        write!(w, "{}", "fourth").unwrap();
397        w.finish()
398    };
399    assert_eq!(&stack[first], "first second");
400    assert_eq!(&stack[second], "third fourth");
401}
402
403#[test]
404fn test_iter() {
405    let mut stack = StrStack::new();
406    stack.push("one");
407    stack.push("two");
408    stack.push("three");
409
410    let v1: Vec<_> = stack.iter().collect();
411    let v2: Vec<_> = stack.iter().rev().collect();
412
413    assert_eq!(&v1[..], &["one", "two", "three"]);
414    assert_eq!(&v2[..], &["three", "two", "one"]);
415}