flatcontainer/impls/
string.rs

1//! A region that stores strings.
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use crate::impls::slice_copy::OwnedRegion;
7use crate::{Push, Region, RegionPreference, ReserveItems};
8
9/// A region to store strings and read `&str`.
10///
11/// Delegates to a region `R` to store `u8` slices. By default, it uses a [`OwnedRegion`], but a
12/// different region can be provided, as long as it absorbs and reads items as `&[u8]`.
13///
14/// Note that all implementations of `Push<T> for StringRegion` must only accept valid utf-8 data
15/// because the region does not validate the contents when indexing.
16///
17/// # Examples
18///
19/// We fill some data into a string region and use extract it later.
20/// ```
21/// use flatcontainer::{RegionPreference, Push, OwnedRegion, Region, StringRegion};
22/// let mut r = <StringRegion>::default();
23///
24/// let panagram_en = "The quick fox jumps over the lazy dog";
25/// let panagram_de = "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich";
26///
27/// let en_index = r.push(panagram_en);
28/// let de_index = r.push(panagram_de);
29///
30/// assert_eq!(panagram_de, r.index(de_index));
31/// assert_eq!(panagram_en, r.index(en_index));
32/// ```
33#[derive(Default, Debug)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35pub struct StringRegion<R = OwnedRegion<u8>> {
36    inner: R,
37}
38
39impl<R: Clone> Clone for StringRegion<R> {
40    fn clone(&self) -> Self {
41        Self {
42            inner: self.inner.clone(),
43        }
44    }
45
46    fn clone_from(&mut self, source: &Self) {
47        self.inner.clone_from(&source.inner);
48    }
49}
50
51impl<R> Region for StringRegion<R>
52where
53    for<'a> R: Region<ReadItem<'a> = &'a [u8]> + 'a,
54{
55    type Owned = String;
56    type ReadItem<'a> = &'a str where Self: 'a ;
57    type Index = R::Index;
58
59    #[inline]
60    fn merge_regions<'a>(regions: impl Iterator<Item = &'a Self> + Clone) -> Self
61    where
62        Self: 'a,
63    {
64        Self {
65            inner: R::merge_regions(regions.map(|r| &r.inner)),
66        }
67    }
68
69    #[inline]
70    fn index(&self, index: Self::Index) -> Self::ReadItem<'_> {
71        // SAFETY: All Push implementations only accept correct utf8 data
72        unsafe { std::str::from_utf8_unchecked(self.inner.index(index)) }
73    }
74
75    #[inline]
76    fn reserve_regions<'a, I>(&mut self, regions: I)
77    where
78        Self: 'a,
79        I: Iterator<Item = &'a Self> + Clone,
80    {
81        self.inner.reserve_regions(regions.map(|r| &r.inner));
82    }
83
84    #[inline]
85    fn clear(&mut self) {
86        self.inner.clear();
87    }
88
89    #[inline]
90    fn heap_size<F: FnMut(usize, usize)>(&self, callback: F) {
91        self.inner.heap_size(callback);
92    }
93
94    #[inline]
95    fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b>
96    where
97        Self: 'a,
98    {
99        item
100    }
101}
102
103impl RegionPreference for String {
104    type Owned = Self;
105    type Region = StringRegion;
106}
107
108impl RegionPreference for &str {
109    type Owned = String;
110    type Region = StringRegion;
111}
112
113impl<R> Push<String> for StringRegion<R>
114where
115    for<'a> R: Region<ReadItem<'a> = &'a [u8]> + Push<&'a [u8]> + 'a,
116{
117    #[inline]
118    fn push(&mut self, item: String) -> <StringRegion<R> as Region>::Index {
119        self.push(item.as_str())
120    }
121}
122
123impl<R> Push<&String> for StringRegion<R>
124where
125    for<'a> R: Region<ReadItem<'a> = &'a [u8]> + Push<&'a [u8]> + 'a,
126{
127    #[inline]
128    fn push(&mut self, item: &String) -> <StringRegion<R> as Region>::Index {
129        self.push(item.as_str())
130    }
131}
132
133impl<'b, R> ReserveItems<&'b String> for StringRegion<R>
134where
135    for<'a> R: Region<ReadItem<'a> = &'a [u8]> + ReserveItems<&'a [u8]> + 'a,
136{
137    #[inline]
138    fn reserve_items<I>(&mut self, items: I)
139    where
140        I: Iterator<Item = &'b String> + Clone,
141    {
142        self.reserve_items(items.map(String::as_str));
143    }
144}
145
146impl<R> Push<&str> for StringRegion<R>
147where
148    for<'a> R: Region<ReadItem<'a> = &'a [u8]> + Push<&'a [u8]> + 'a,
149{
150    #[inline]
151    fn push(&mut self, item: &str) -> <StringRegion<R> as Region>::Index {
152        self.inner.push(item.as_bytes())
153    }
154}
155
156impl<R> Push<&&str> for StringRegion<R>
157where
158    for<'a> R: Region<ReadItem<'a> = &'a [u8]> + Push<&'a [u8]> + 'a,
159{
160    #[inline]
161    fn push(&mut self, item: &&str) -> <StringRegion<R> as Region>::Index {
162        self.push(*item)
163    }
164}
165
166impl<'b, R> ReserveItems<&'b str> for StringRegion<R>
167where
168    for<'a> R: Region<ReadItem<'a> = &'a [u8]> + ReserveItems<&'a [u8]> + 'a,
169{
170    #[inline]
171    fn reserve_items<I>(&mut self, items: I)
172    where
173        I: Iterator<Item = &'b str> + Clone,
174    {
175        self.inner.reserve_items(items.map(str::as_bytes));
176    }
177}
178
179impl<'a, 'b: 'a, R> ReserveItems<&'a &'b str> for StringRegion<R>
180where
181    for<'c> R: Region<ReadItem<'c> = &'c [u8]> + ReserveItems<&'c [u8]> + 'c,
182{
183    #[inline]
184    fn reserve_items<I>(&mut self, items: I)
185    where
186        I: Iterator<Item = &'a &'b str> + Clone,
187    {
188        self.reserve_items(items.copied());
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use crate::{IntoOwned, Push, Region, ReserveItems, StringRegion};
195
196    #[test]
197    fn test_inner() {
198        let mut r = <StringRegion>::default();
199        let index = r.push("abc");
200        assert_eq!(r.index(index), "abc");
201    }
202
203    #[test]
204    fn test_reserve_items_str() {
205        let mut r = <StringRegion>::default();
206        r.reserve_items(std::iter::repeat("abc").take(1000));
207
208        let (mut cap, mut cnt) = (0, 0);
209        r.heap_size(|_, c| {
210            cap += c;
211            cnt += 1;
212        });
213
214        assert!(cap > 0);
215        assert!(cnt > 0);
216    }
217
218    #[test]
219    fn test_reserve_items_ref_str() {
220        let mut r = <StringRegion>::default();
221        r.reserve_items(std::iter::repeat(&"abc").take(1000));
222
223        let (mut cap, mut cnt) = (0, 0);
224        r.heap_size(|_, c| {
225            cap += c;
226            cnt += 1;
227        });
228
229        assert!(cap > 0);
230        assert!(cnt > 0);
231    }
232
233    #[test]
234    fn test_reserve_items_string() {
235        let mut r = <StringRegion>::default();
236        r.reserve_items(std::iter::repeat(&"abc".to_owned()).take(1000));
237
238        let (mut cap, mut cnt) = (0, 0);
239        r.heap_size(|_, c| {
240            cap += c;
241            cnt += 1;
242        });
243
244        assert!(cap > 0);
245        assert!(cnt > 0);
246    }
247
248    #[test]
249    fn owned() {
250        let mut r = <StringRegion>::default();
251
252        let idx = r.push("abc");
253        let reference = r.index(idx);
254        let owned = reference.into_owned();
255        let idx = r.push(owned);
256        assert_eq!("abc", r.index(idx));
257    }
258}