1 pub const MAX_LENGTH: usize = 255;
2 
3 /// A string of length at most 255, with a more compact on-disk
4 /// encoding.
5 #[repr(packed)]
6 pub struct SmallString {
7     pub len: u8,
8     pub str: [u8; MAX_LENGTH],
9 }
10 
11 /// A borrowed version of `SmallStr`.
12 pub struct SmallStr {
13     len: u8,
14     _str: [u8],
15 }
16 
17 impl std::hash::Hash for SmallStr {
hash<H: std::hash::Hasher>(&self, hasher: &mut H)18     fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
19         self.as_bytes().hash(hasher)
20     }
21 }
22 
23 impl Clone for SmallString {
clone(&self) -> Self24     fn clone(&self) -> Self {
25         Self::from_str(self.as_str())
26     }
27 }
28 
29 impl std::fmt::Debug for SmallString {
fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result30     fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
31         use std::ops::Deref;
32         self.deref().fmt(fmt)
33     }
34 }
35 
36 impl PartialEq for SmallStr {
eq(&self, x: &SmallStr) -> bool37     fn eq(&self, x: &SmallStr) -> bool {
38         self.as_str().eq(x.as_str())
39     }
40 }
41 
42 impl std::ops::Deref for SmallString {
43     type Target = SmallStr;
deref(&self) -> &Self::Target44     fn deref(&self) -> &Self::Target {
45         let len = self.len as usize;
46         unsafe {
47             std::mem::transmute(std::slice::from_raw_parts(
48                 self as *const Self as *const u8,
49                 1 + len,
50             ))
51         }
52     }
53 }
54 
55 impl AsRef<SmallStr> for SmallString {
as_ref(&self) -> &SmallStr56     fn as_ref(&self) -> &SmallStr {
57         let len = self.len as usize;
58         unsafe {
59             std::mem::transmute(std::slice::from_raw_parts(
60                 self as *const Self as *const u8,
61                 1 + len,
62             ))
63         }
64     }
65 }
66 
67 impl AsMut<SmallStr> for SmallString {
as_mut(&mut self) -> &mut SmallStr68     fn as_mut(&mut self) -> &mut SmallStr {
69         let len = self.len as usize;
70         unsafe {
71             std::mem::transmute(std::slice::from_raw_parts_mut(
72                 self as *mut Self as *mut u8,
73                 1 + len,
74             ))
75         }
76     }
77 }
78 
79 impl std::ops::DerefMut for SmallString {
deref_mut(&mut self) -> &mut Self::Target80     fn deref_mut(&mut self) -> &mut Self::Target {
81         let len = self.len as usize;
82         unsafe {
83             std::mem::transmute(std::slice::from_raw_parts_mut(
84                 self as *mut Self as *mut u8,
85                 1 + len,
86             ))
87         }
88     }
89 }
90 
91 #[test]
eq()92 fn eq() {
93     let s0 = SmallString::from_str("blabla");
94     let s1 = SmallString::from_str("blabla");
95     assert_eq!(s0, s1);
96 
97     assert_eq!(s0, s1);
98 
99     assert_eq!(s0, s1);
100     assert_eq!(s0, s0);
101     assert_eq!(s1, s1);
102 }
103 
104 #[test]
debug()105 fn debug() {
106     let s = SmallString::from_str("blabla");
107     assert_eq!(format!("{:?}", s), "\"blabla\"");
108 }
109 
110 impl Eq for SmallStr {}
111 
112 impl PartialEq for SmallString {
eq(&self, x: &SmallString) -> bool113     fn eq(&self, x: &SmallString) -> bool {
114         self.as_str().eq(x.as_str())
115     }
116 }
117 impl Eq for SmallString {}
118 
119 impl std::hash::Hash for SmallString {
hash<H: std::hash::Hasher>(&self, x: &mut H)120     fn hash<H: std::hash::Hasher>(&self, x: &mut H) {
121         self.as_str().hash(x)
122     }
123 }
124 
125 impl PartialOrd for SmallStr {
partial_cmp(&self, x: &SmallStr) -> Option<std::cmp::Ordering>126     fn partial_cmp(&self, x: &SmallStr) -> Option<std::cmp::Ordering> {
127         self.as_str().partial_cmp(x.as_str())
128     }
129 }
130 impl Ord for SmallStr {
cmp(&self, x: &SmallStr) -> std::cmp::Ordering131     fn cmp(&self, x: &SmallStr) -> std::cmp::Ordering {
132         self.as_str().cmp(x.as_str())
133     }
134 }
135 
136 impl PartialOrd for SmallString {
partial_cmp(&self, x: &SmallString) -> Option<std::cmp::Ordering>137     fn partial_cmp(&self, x: &SmallString) -> Option<std::cmp::Ordering> {
138         self.as_str().partial_cmp(x.as_str())
139     }
140 }
141 impl Ord for SmallString {
cmp(&self, x: &SmallString) -> std::cmp::Ordering142     fn cmp(&self, x: &SmallString) -> std::cmp::Ordering {
143         self.as_str().cmp(x.as_str())
144     }
145 }
146 
147 #[test]
ord()148 fn ord() {
149     let s0 = SmallString::from_str("1234");
150     let s1 = SmallString::from_str("5678");
151     assert!(s0 < s1);
152     assert!(s0 < s1);
153     assert_eq!(s0.cmp(&s1), std::cmp::Ordering::Less);
154 }
155 
156 impl std::fmt::Debug for SmallStr {
fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result157     fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
158         self.as_str().fmt(fmt)
159     }
160 }
161 
162 impl Default for SmallString {
default() -> Self163     fn default() -> Self {
164         Self {
165             len: 0,
166             str: [0; MAX_LENGTH],
167         }
168     }
169 }
170 
171 impl SmallString {
new() -> Self172     pub fn new() -> Self {
173         Self::default()
174     }
175     /// ```ignore
176     /// use libpijul::small_string::*;
177     /// let mut s = SmallString::from_str("blah!");
178     /// assert_eq!(s.len(), s.as_str().len());
179     /// ```
len(&self) -> usize180     pub fn len(&self) -> usize {
181         self.len as usize
182     }
183 
184     /// ```ignore
185     /// use libpijul::small_string::*;
186     /// let mut s = SmallString::from_str("blah");
187     /// s.clear();
188     /// assert_eq!(s.as_str(), "");
189     /// assert!(s.is_empty());
190     /// ```
is_empty(&self) -> bool191     pub fn is_empty(&self) -> bool {
192         self.len() == 0
193     }
194 
from_str(s: &str) -> Self195     pub fn from_str(s: &str) -> Self {
196         let mut b = SmallString {
197             len: s.len() as u8,
198             str: [0; MAX_LENGTH],
199         };
200         b.clone_from_str(s);
201         b
202     }
clone_from_str(&mut self, s: &str)203     pub fn clone_from_str(&mut self, s: &str) {
204         self.len = s.len() as u8;
205         (&mut self.str[..s.len()]).copy_from_slice(s.as_bytes());
206     }
207 
208     /// ```ignore
209     /// use libpijul::small_string::*;
210     /// let mut s = SmallString::from_str("blah");
211     /// s.clear();
212     /// assert!(s.is_empty());
213     /// ```
clear(&mut self)214     pub fn clear(&mut self) {
215         self.len = 0;
216     }
push_str(&mut self, s: &str)217     pub fn push_str(&mut self, s: &str) {
218         let l = self.len as usize;
219         assert!(l + s.len() <= 0xff);
220         (&mut self.str[l..l + s.len()]).copy_from_slice(s.as_bytes());
221         self.len += s.len() as u8;
222     }
223 
as_str(&self) -> &str224     pub fn as_str(&self) -> &str {
225         use std::ops::Deref;
226         self.deref().as_str()
227     }
228 
as_bytes(&self) -> &[u8]229     pub fn as_bytes(&self) -> &[u8] {
230         use std::ops::Deref;
231         self.deref().as_bytes()
232     }
233 }
234 /*
235 impl SmallStr {
236     pub const EMPTY: &'static SmallStr = &SmallStr {
237         len: 0,
238         str: [][..]
239     };
240 }
241 */
242 impl SmallStr {
243     /// ```ignore
244     /// use libpijul::small_string::*;
245     /// let mut s = SmallString::from_str("");
246     /// assert!(s.as_small_str().is_empty());
247     /// s.push_str("blah");
248     /// assert!(!s.as_small_str().is_empty());
249     /// ```
is_empty(&self) -> bool250     pub fn is_empty(&self) -> bool {
251         self.len() == 0
252     }
253 
254     /// ```ignore
255     /// use libpijul::small_string::*;
256     /// let mut s = SmallString::from_str("blah");
257     /// assert_eq!(s.as_small_str().len(), "blah".len())
258     /// ```
len(&self) -> usize259     pub fn len(&self) -> usize {
260         self.len as usize
261     }
262 
as_str(&self) -> &str263     pub fn as_str(&self) -> &str {
264         unsafe { std::str::from_utf8_unchecked(self.as_bytes()) }
265     }
266 
as_bytes(&self) -> &[u8]267     pub fn as_bytes(&self) -> &[u8] {
268         let s: &[u8] = unsafe { std::mem::transmute(self) };
269         &s[1..]
270     }
271 
to_owned(&self) -> SmallString272     pub fn to_owned(&self) -> SmallString {
273         SmallString::from_str(self.as_str())
274     }
275 }
276 
277 /// Faster than running doc tests.
278 #[test]
all_doc_tests()279 fn all_doc_tests() {
280     {
281         let s = SmallString::from_str("blah!");
282         assert_eq!(s.len(), s.as_str().len());
283     }
284     {
285         let mut s = SmallString::from_str("blah");
286         s.clear();
287         assert_eq!(s.as_str(), "");
288         assert!(s.is_empty());
289     }
290     {
291         let mut s = SmallString::from_str("blah");
292         s.clear();
293         assert!(s.is_empty());
294     }
295     {
296         let mut s = SmallString::from_str("");
297         assert!(s.is_empty());
298         s.push_str("blah");
299         assert!(!s.is_empty());
300     }
301     {
302         let s = SmallString::from_str("blah");
303         assert_eq!(s.len(), "blah".len())
304     }
305 }
306 
307 impl sanakirja::UnsizedStorable for SmallStr {
308     const ALIGN: usize = 1;
309 
size(&self) -> usize310     fn size(&self) -> usize {
311         1 + self.len as usize
312     }
write_to_page(&self, p: *mut u8)313     unsafe fn write_to_page(&self, p: *mut u8) {
314         std::ptr::copy(&self.len, p, 1 + self.len as usize);
315         debug!(
316             "writing {:?}",
317             std::slice::from_raw_parts(p, 1 + self.len as usize)
318         );
319     }
from_raw_ptr<'a, T>(_: &T, p: *const u8) -> &'a Self320     unsafe fn from_raw_ptr<'a, T>(_: &T, p: *const u8) -> &'a Self {
321         smallstr_from_raw_ptr(p)
322     }
onpage_size(p: *const u8) -> usize323     unsafe fn onpage_size(p: *const u8) -> usize {
324         let len = *p as usize;
325         debug!(
326             "onpage_size {:?}",
327             std::slice::from_raw_parts(p, 1 + len as usize)
328         );
329         1 + len
330     }
331 }
332 
333 impl sanakirja::Storable for SmallStr {
compare<T>(&self, _: &T, x: &Self) -> std::cmp::Ordering334     fn compare<T>(&self, _: &T, x: &Self) -> std::cmp::Ordering {
335         self.cmp(x)
336     }
337     type PageReferences = std::iter::Empty<u64>;
page_references(&self) -> Self::PageReferences338     fn page_references(&self) -> Self::PageReferences {
339         std::iter::empty()
340     }
341 }
342 
smallstr_from_raw_ptr<'a>(p: *const u8) -> &'a SmallStr343 unsafe fn smallstr_from_raw_ptr<'a>(p: *const u8) -> &'a SmallStr {
344     let len = *p as usize;
345     std::mem::transmute(std::slice::from_raw_parts(p, 1 + len as usize))
346 }
347 
348 #[test]
smallstr_repr()349 fn smallstr_repr() {
350     use sanakirja::UnsizedStorable;
351     let o = SmallString::from_str("blablabla");
352     let mut x = vec![0u8; 200];
353     unsafe {
354         o.write_to_page(x.as_mut_ptr());
355         let p = smallstr_from_raw_ptr(x.as_ptr());
356         assert_eq!(p.as_str(), "blablabla")
357     }
358 }
359