pijul_core/
small_string.rs1pub const MAX_LENGTH: usize = 255;
2
3#[repr(C)]
6pub struct SmallString {
7 pub len: u8,
8 pub str: [u8; MAX_LENGTH],
9}
10
11#[repr(C)]
13pub struct SmallStr {
14 len: u8,
15 _str: [u8],
16}
17
18impl std::hash::Hash for SmallStr {
19 fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
20 self.as_bytes().hash(hasher)
21 }
22}
23
24impl Clone for SmallString {
25 fn clone(&self) -> Self {
26 SmallString {
27 len: self.len,
28 str: self.str,
29 }
30 }
31}
32
33impl std::fmt::Debug for SmallString {
34 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
35 use std::ops::Deref;
36 self.deref().fmt(fmt)
37 }
38}
39
40impl std::fmt::Display for SmallString {
41 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
42 use std::ops::Deref;
43 self.deref().fmt(fmt)
44 }
45}
46
47impl PartialEq for SmallStr {
48 fn eq(&self, x: &SmallStr) -> bool {
49 self.as_str().eq(x.as_str())
50 }
51}
52
53impl std::ops::Deref for SmallString {
54 type Target = SmallStr;
55 fn deref(&self) -> &Self::Target {
56 let len = self.len as usize;
57 unsafe {
58 std::mem::transmute(std::slice::from_raw_parts(
59 self as *const Self as *const u8,
60 1 + len,
61 ))
62 }
63 }
64}
65
66impl AsRef<SmallStr> for SmallString {
67 fn as_ref(&self) -> &SmallStr {
68 let len = self.len as usize;
69 unsafe {
70 std::mem::transmute(std::slice::from_raw_parts(
71 self as *const Self as *const u8,
72 1 + len,
73 ))
74 }
75 }
76}
77
78impl AsMut<SmallStr> for SmallString {
79 fn as_mut(&mut self) -> &mut SmallStr {
80 let len = self.len as usize;
81 unsafe {
82 std::mem::transmute(std::slice::from_raw_parts_mut(
83 self as *mut Self as *mut u8,
84 1 + len,
85 ))
86 }
87 }
88}
89
90impl std::ops::DerefMut for SmallString {
91 fn deref_mut(&mut self) -> &mut Self::Target {
92 let len = self.len as usize;
93 unsafe {
94 std::mem::transmute(std::slice::from_raw_parts_mut(
95 self as *mut Self as *mut u8,
96 1 + len,
97 ))
98 }
99 }
100}
101
102#[test]
103fn eq() {
104 let s0 = SmallString::from_str("blabla");
105 let s1 = SmallString::from_str("blabla");
106 assert_eq!(s0, s1);
107
108 assert_eq!(s0, s1);
109
110 assert_eq!(s0, s1);
111 assert_eq!(s0, s0);
112 assert_eq!(s1, s1);
113}
114
115#[test]
116fn debug() {
117 let s = SmallString::from_str("blabla");
118 assert_eq!(format!("{:?}", s), "\"blabla\"");
119}
120
121impl Eq for SmallStr {}
122
123impl PartialEq for SmallString {
124 fn eq(&self, x: &SmallString) -> bool {
125 self.as_str().eq(x.as_str())
126 }
127}
128impl Eq for SmallString {}
129
130impl std::hash::Hash for SmallString {
131 fn hash<H: std::hash::Hasher>(&self, x: &mut H) {
132 self.as_str().hash(x)
133 }
134}
135
136impl PartialOrd for SmallStr {
137 fn partial_cmp(&self, x: &SmallStr) -> Option<std::cmp::Ordering> {
138 Some(self.cmp(x))
139 }
140}
141impl Ord for SmallStr {
142 fn cmp(&self, x: &SmallStr) -> std::cmp::Ordering {
143 self.as_str().cmp(x.as_str())
144 }
145}
146
147impl PartialOrd for SmallString {
148 fn partial_cmp(&self, x: &SmallString) -> Option<std::cmp::Ordering> {
149 Some(self.cmp(x))
150 }
151}
152impl Ord for SmallString {
153 fn cmp(&self, x: &SmallString) -> std::cmp::Ordering {
154 self.as_str().cmp(x.as_str())
155 }
156}
157
158#[test]
159fn ord() {
160 let s0 = SmallString::from_str("1234");
161 let s1 = SmallString::from_str("5678");
162 assert!(s0 < s1);
163 assert!(s0 < s1);
164 assert_eq!(s0.cmp(&s1), std::cmp::Ordering::Less);
165}
166
167impl std::fmt::Debug for SmallStr {
168 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
169 self.as_str().fmt(fmt)
170 }
171}
172
173impl std::fmt::Display for SmallStr {
174 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
175 self.as_str().fmt(fmt)
176 }
177}
178
179impl Default for SmallString {
180 fn default() -> Self {
181 Self {
182 len: 0,
183 str: [0; MAX_LENGTH],
184 }
185 }
186}
187
188#[derive(Debug, Error)]
189pub enum Error {
190 #[error("Small string too long: {length}")]
191 TooLong { length: usize },
192}
193
194impl std::str::FromStr for SmallString {
195 type Err = Error;
196
197 fn from_str(s: &str) -> Result<Self, Error> {
198 if s.len() > MAX_LENGTH {
199 return Err(Error::TooLong { length: s.len() });
200 }
201 let mut b = SmallString {
202 len: s.len() as u8,
203 str: [0; MAX_LENGTH],
204 };
205 b.clone_from_str(s);
206 Ok(b)
207 }
208}
209
210impl SmallString {
211 pub fn new() -> Self {
212 Self::default()
213 }
214
215 pub fn from_str(s: &str) -> Self {
216 <SmallString as std::str::FromStr>::from_str(s).expect("string too long for SmallString")
217 }
218 pub fn len(&self) -> usize {
224 self.len as usize
225 }
226
227 pub fn is_empty(&self) -> bool {
235 self.len() == 0
236 }
237
238 pub fn clone_from_str(&mut self, s: &str) {
239 self.len = s.len() as u8;
240 self.str[..s.len()].copy_from_slice(s.as_bytes());
241 }
242
243 pub fn clear(&mut self) {
250 self.len = 0;
251 }
252 pub fn push_str(&mut self, s: &str) {
253 let l = self.len as usize;
254 assert!(l + s.len() <= 0xff);
255 self.str[l..l + s.len()].copy_from_slice(s.as_bytes());
256 self.len += s.len() as u8;
257 }
258
259 pub fn as_str(&self) -> &str {
260 use std::ops::Deref;
261 self.deref().as_str()
262 }
263
264 pub fn as_bytes(&self) -> &[u8] {
265 use std::ops::Deref;
266 self.deref().as_bytes()
267 }
268}
269
270impl SmallStr {
271 pub fn is_empty(&self) -> bool {
279 self.len() == 0
280 }
281
282 pub fn len(&self) -> usize {
288 self.len as usize
289 }
290
291 pub fn as_str(&self) -> &str {
292 unsafe { std::str::from_utf8_unchecked(self.as_bytes()) }
293 }
294
295 pub fn as_bytes(&self) -> &[u8] {
296 let s: &[u8] = unsafe { std::mem::transmute(self) };
297 &s[1..]
298 }
299
300 pub fn to_owned(&self) -> SmallString {
301 let mut str = [0; 255];
302 str[..self.len as usize].clone_from_slice(&self._str[..self.len as usize]);
303 SmallString { len: self.len, str }
304 }
305}
306
307#[test]
309fn all_doc_tests() {
310 {
311 let s = SmallString::from_str("blah!");
312 assert_eq!(s.len(), s.as_str().len());
313 }
314 {
315 let mut s = SmallString::from_str("blah");
316 s.clear();
317 assert_eq!(s.as_str(), "");
318 assert!(s.is_empty());
319 }
320 {
321 let mut s = SmallString::from_str("blah");
322 s.clear();
323 assert!(s.is_empty());
324 }
325 {
326 let mut s = SmallString::from_str("");
327 assert!(s.is_empty());
328 s.push_str("blah");
329 assert!(!s.is_empty());
330 }
331 {
332 let s = SmallString::from_str("blah");
333 assert_eq!(s.len(), "blah".len())
334 }
335}
336
337impl sanakirja::UnsizedStorable for SmallStr {
338 const ALIGN: usize = 1;
339
340 fn size(&self) -> usize {
341 1 + self.len as usize
342 }
343 unsafe fn write_to_page(&self, p: *mut u8) {
344 unsafe {
345 std::ptr::copy(&self.len, p, 1 + self.len as usize);
346 debug!(
347 "writing {:?}",
348 std::slice::from_raw_parts(p, 1 + self.len as usize)
349 );
350 }
351 }
352 unsafe fn from_raw_ptr<'a, T>(_: &T, p: *const u8) -> &'a Self {
353 unsafe { smallstr_from_raw_ptr(p) }
354 }
355 unsafe fn onpage_size(p: *const u8) -> usize {
356 unsafe {
357 let len = *p as usize;
358 debug!("onpage_size {:?}", std::slice::from_raw_parts(p, 1 + len));
359 1 + len
360 }
361 }
362}
363
364impl sanakirja::Storable for SmallStr {
365 fn compare<T>(&self, _: &T, x: &Self) -> std::cmp::Ordering {
366 self.cmp(x)
367 }
368 type PageReferences = std::iter::Empty<u64>;
369 fn page_references(&self) -> Self::PageReferences {
370 std::iter::empty()
371 }
372}
373
374impl ::sanakirja::debug::Check for SmallStr {}
375
376unsafe fn smallstr_from_raw_ptr<'a>(p: *const u8) -> &'a SmallStr {
377 unsafe {
378 let len = *p as usize;
379 std::mem::transmute(std::slice::from_raw_parts(p, 1 + len))
380 }
381}
382
383#[test]
384fn smallstr_repr() {
385 use sanakirja::UnsizedStorable;
386 let o = SmallString::from_str("blablabla");
387 let mut x = vec![0u8; 200];
388 unsafe {
389 o.write_to_page(x.as_mut_ptr());
390 let p = smallstr_from_raw_ptr(x.as_ptr());
391 assert_eq!(p.as_str(), "blablabla")
392 }
393}