1use std::convert::TryInto;
2use std::hint::unreachable_unchecked;
3use std::iter::{ExactSizeIterator, IntoIterator, Iterator};
4use std::slice;
5use std::str;
6
7use thin_vec::ThinVec;
8
9#[derive(Debug, Default, Eq, PartialEq, Clone, Hash)]
14pub struct Strings {
15 strs: ThinVec<u8>,
16 ends: ThinVec<u32>,
17}
18
19impl Strings {
20 #[inline(always)]
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn with_capacity(len: u32) -> Self {
27 let mut strings = Self::default();
28 strings.reserve(len);
29 strings
30 }
31
32 pub fn push(&mut self, s: &str) {
34 self.strs.extend_from_slice(s.as_bytes());
35 self.ends.push(
36 self.strs
37 .len()
38 .try_into()
39 .expect("Strings cannot contain more than u32::MAX strings"),
40 );
41 }
42
43 #[inline(always)]
45 pub fn strs_len(&self) -> u32 {
46 match self.strs.len().try_into() {
47 Ok(len) => len,
48 Err(_err) => unsafe { unreachable_unchecked() },
49 }
50 }
51
52 #[inline(always)]
53 pub fn len(&self) -> u32 {
54 match self.ends.len().try_into() {
55 Ok(len) => len,
56 Err(_err) => unsafe { unreachable_unchecked() },
57 }
58 }
59
60 #[inline(always)]
61 pub fn is_empty(&self) -> bool {
62 self.len() == 0
63 }
64
65 #[inline(always)]
66 pub fn reserve(&mut self, strs_cnt: u32) {
67 self.ends.reserve(strs_cnt as usize);
68 }
69
70 #[inline(always)]
71 pub fn reserve_strs(&mut self, cnt: usize) {
72 self.strs.reserve(cnt);
73 }
74
75 pub fn shrink_to_fit(&mut self) {
76 self.strs.shrink_to_fit();
77 self.ends.shrink_to_fit();
78 }
79
80 #[inline(always)]
81 pub fn iter(&self) -> StringsIter<'_> {
82 StringsIter {
83 strings: self,
84 ends_iter: self.ends.iter(),
85 start: 0,
86 }
87 }
88
89 pub fn get(&self, index: u32) -> Option<&str> {
90 let end = *self.ends.get(index as usize)?;
91 let start = if index == 0 {
92 0
93 } else {
94 self.ends[(index - 1) as usize]
95 };
96
97 Some(self.get_str_impl(start, end))
98 }
99
100 #[inline(always)]
101 fn get_str_impl(&self, start: u32, end: u32) -> &str {
102 unsafe { str::from_utf8_unchecked(&self.strs[(start as usize)..(end as usize)]) }
103 }
104
105 pub fn as_str(&self) -> &str {
106 self.get_str_impl(0, self.strs_len())
107 }
108
109 pub fn into_str(self) -> String {
110 let mut vec = Vec::with_capacity(self.strs.len());
111 vec.extend_from_slice(&self.strs);
112 unsafe { String::from_utf8_unchecked(vec) }
113 }
114}
115impl<'a> IntoIterator for &'a Strings {
116 type Item = &'a str;
117 type IntoIter = StringsIter<'a>;
118
119 #[inline(always)]
120 fn into_iter(self) -> Self::IntoIter {
121 self.iter()
122 }
123}
124
125#[derive(Clone, Debug)]
126pub struct StringsIter<'a> {
127 strings: &'a Strings,
128 ends_iter: slice::Iter<'a, u32>,
129 start: u32,
130}
131
132impl<'a> Iterator for StringsIter<'a> {
133 type Item = &'a str;
134
135 fn next(&mut self) -> Option<Self::Item> {
136 let start = self.start;
137 let end = *self.ends_iter.next()?;
138
139 self.start = end;
140
141 Some(self.strings.get_str_impl(start, end))
142 }
143
144 fn size_hint(&self) -> (usize, Option<usize>) {
145 let len = self.ends_iter.len();
146 (len, Some(len))
147 }
148}
149
150impl ExactSizeIterator for StringsIter<'_> {}
151
152#[cfg(test)]
153mod tests {
154 use super::Strings;
155 use std::convert::TryInto;
156
157 fn assert_strs_in(strs: &Strings, input_strs: &Vec<String>) {
158 for (string, input_str) in strs.iter().zip(input_strs) {
159 assert_eq!(string, input_str);
160 }
161 }
162
163 #[test]
164 fn test() {
165 let mut strs = Strings::new();
166 let input_strs: Vec<String> = (0..256).map(|n| n.to_string()).collect();
167
168 assert!(strs.is_empty());
169
170 for (i, input_str) in input_strs.iter().enumerate() {
171 strs.push(input_str);
172 assert_eq!(strs.len() as usize, i + 1);
173
174 assert_strs_in(&strs, &input_strs);
175 }
176
177 assert!(input_strs.iter().eq(strs.iter()));
178
179 for (i, input_str) in input_strs.iter().enumerate() {
180 assert_eq!(strs.get(i.try_into().unwrap()).unwrap(), input_str);
181 }
182
183 let input_str = input_strs.concat();
184
185 assert_eq!(strs.as_str(), input_str);
186 assert_eq!(strs.into_str(), input_str);
187 }
188}