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