1use alloc::borrow;
38use core::fmt;
39use core::hash;
40use core::ops;
41use core::ptr;
42use core::str;
43
44
45#[cfg(target_pointer_width = "64")]
51pub const INLINE_STRING_CAPACITY: usize = 30;
52#[cfg(target_pointer_width = "32")]
53pub const INLINE_STRING_CAPACITY: usize = 14;
54
55#[derive(Clone, Debug, Eq)]
59pub struct InlineString {
60 length: u8,
61 bytes: [u8; INLINE_STRING_CAPACITY],
62}
63
64#[derive(Debug, PartialEq)]
67pub struct NotEnoughSpaceError;
68
69impl AsRef<str> for InlineString {
70 fn as_ref(&self) -> &str {
71 self.assert_sanity();
72 unsafe { str::from_utf8_unchecked(&self.bytes[..self.len()]) }
73 }
74}
75
76impl AsRef<[u8]> for InlineString {
77 #[inline]
78 fn as_ref(&self) -> &[u8] {
79 self.as_bytes()
80 }
81}
82
83impl AsMut<str> for InlineString {
84 fn as_mut(&mut self) -> &mut str {
85 self.assert_sanity();
86 let length = self.len();
87 unsafe { str::from_utf8_unchecked_mut(&mut self.bytes[..length]) }
88 }
89}
90
91impl AsMut<[u8]> for InlineString {
92 #[inline]
93 fn as_mut(&mut self) -> &mut [u8] {
94 self.assert_sanity();
95 let length = self.len();
96 &mut self.bytes[0..length]
97 }
98}
99
100impl<'a> From<&'a str> for InlineString {
107 fn from(string: &'a str) -> InlineString {
108 let string_len = string.len();
109 assert!(string_len <= INLINE_STRING_CAPACITY);
110
111 let mut ss = InlineString::new();
112 unsafe {
113 ptr::copy_nonoverlapping(string.as_ptr(), ss.bytes.as_mut_ptr(), string_len);
114 }
115 ss.length = string_len as u8;
116
117 ss.assert_sanity();
118 ss
119 }
120}
121
122impl fmt::Display for InlineString {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
124 self.assert_sanity();
125 write!(f, "{}", self as &str)
126 }
127}
128
129impl fmt::Write for InlineString {
130 fn write_char(&mut self, ch: char) -> Result<(), fmt::Error> {
131 self.push(ch).map_err(|_| fmt::Error)
132 }
133 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
134 self.push_str(s).map_err(|_| fmt::Error)
135 }
136}
137
138impl hash::Hash for InlineString {
139 #[inline]
140 fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
141 (**self).hash(hasher)
142 }
143}
144
145impl ops::Index<ops::Range<usize>> for InlineString {
146 type Output = str;
147
148 #[inline]
149 fn index(&self, index: ops::Range<usize>) -> &str {
150 self.assert_sanity();
151 &self[..][index]
152 }
153}
154
155impl ops::Index<ops::RangeTo<usize>> for InlineString {
156 type Output = str;
157
158 #[inline]
159 fn index(&self, index: ops::RangeTo<usize>) -> &str {
160 self.assert_sanity();
161 &self[..][index]
162 }
163}
164
165impl ops::Index<ops::RangeFrom<usize>> for InlineString {
166 type Output = str;
167
168 #[inline]
169 fn index(&self, index: ops::RangeFrom<usize>) -> &str {
170 self.assert_sanity();
171 &self[..][index]
172 }
173}
174
175impl ops::Index<ops::RangeFull> for InlineString {
176 type Output = str;
177
178 #[inline]
179 fn index(&self, _index: ops::RangeFull) -> &str {
180 self.assert_sanity();
181 unsafe { str::from_utf8_unchecked(&self.bytes[..self.len()]) }
182 }
183}
184
185impl ops::IndexMut<ops::Range<usize>> for InlineString {
186 #[inline]
187 fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
188 self.assert_sanity();
189 &mut self[..][index]
190 }
191}
192
193impl ops::IndexMut<ops::RangeTo<usize>> for InlineString {
194 #[inline]
195 fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
196 self.assert_sanity();
197 &mut self[..][index]
198 }
199}
200
201impl ops::IndexMut<ops::RangeFrom<usize>> for InlineString {
202 #[inline]
203 fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
204 self.assert_sanity();
205 &mut self[..][index]
206 }
207}
208
209impl ops::IndexMut<ops::RangeFull> for InlineString {
210 #[inline]
211 fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str {
212 self.assert_sanity();
213 let length = self.len();
214 unsafe { str::from_utf8_unchecked_mut(&mut self.bytes[..length]) }
215 }
216}
217
218impl ops::Deref for InlineString {
219 type Target = str;
220
221 #[inline]
222 fn deref(&self) -> &str {
223 self.assert_sanity();
224 unsafe { str::from_utf8_unchecked(&self.bytes[..self.len()]) }
225 }
226}
227
228impl ops::DerefMut for InlineString {
229 #[inline]
230 fn deref_mut(&mut self) -> &mut str {
231 self.assert_sanity();
232 let length = self.len();
233 unsafe { str::from_utf8_unchecked_mut(&mut self.bytes[..length]) }
234 }
235}
236
237impl Default for InlineString {
238 #[inline]
239 fn default() -> InlineString {
240 InlineString::new()
241 }
242}
243
244impl PartialEq<InlineString> for InlineString {
245 #[inline]
246 fn eq(&self, rhs: &InlineString) -> bool {
247 self.assert_sanity();
248 rhs.assert_sanity();
249 PartialEq::eq(&self[..], &rhs[..])
250 }
251}
252
253macro_rules! impl_eq {
254 ($lhs:ty, $rhs: ty) => {
255 impl<'a> PartialEq<$rhs> for $lhs {
256 #[inline]
257 fn eq(&self, other: &$rhs) -> bool {
258 PartialEq::eq(&self[..], &other[..])
259 }
260 }
261
262 impl<'a> PartialEq<$lhs> for $rhs {
263 #[inline]
264 fn eq(&self, other: &$lhs) -> bool {
265 PartialEq::eq(&self[..], &other[..])
266 }
267 }
268 };
269}
270
271impl_eq! { InlineString, str }
272impl_eq! { InlineString, &'a str }
273impl_eq! { borrow::Cow<'a, str>, InlineString }
274
275impl InlineString {
276 #[cfg_attr(feature = "nightly", allow(inline_always))]
277 #[inline(always)]
278 fn assert_sanity(&self) {
279 debug_assert!(
280 self.length as usize <= INLINE_STRING_CAPACITY,
281 "inlinable_string: internal error: length greater than capacity"
282 );
283 debug_assert!(
284 str::from_utf8(&self.bytes[0..self.length as usize]).is_ok(),
285 "inlinable_string: internal error: contents are not valid UTF-8!"
286 );
287 }
288
289 #[inline]
299 pub fn new() -> InlineString {
300 InlineString {
301 length: 0,
302 bytes: [0; INLINE_STRING_CAPACITY],
303 }
304 }
305
306 #[inline]
319 pub fn into_bytes(mut self) -> [u8; INLINE_STRING_CAPACITY] {
320 self.assert_sanity();
321 for i in self.len()..INLINE_STRING_CAPACITY {
322 self.bytes[i] = 0;
323 }
324 self.bytes
325 }
326
327 #[inline]
339 pub fn push_str(&mut self, string: &str) -> Result<(), NotEnoughSpaceError> {
340 self.assert_sanity();
341
342 let string_len = string.len();
343 let new_length = self.len() + string_len;
344
345 if new_length > INLINE_STRING_CAPACITY {
346 return Err(NotEnoughSpaceError);
347 }
348
349 unsafe {
350 ptr::copy_nonoverlapping(
351 string.as_ptr(),
352 self.bytes.as_mut_ptr().offset(self.length as isize),
353 string_len,
354 );
355 }
356 self.length = new_length as u8;
357
358 self.assert_sanity();
359 Ok(())
360 }
361
362 #[inline]
376 pub fn push(&mut self, ch: char) -> Result<(), NotEnoughSpaceError> {
377 self.assert_sanity();
378
379 let char_len = ch.len_utf8();
380 let new_length = self.len() + char_len;
381
382 if new_length > INLINE_STRING_CAPACITY {
383 return Err(NotEnoughSpaceError);
384 }
385
386 {
387 let mut slice = &mut self.bytes[self.length as usize..INLINE_STRING_CAPACITY];
388 ch.encode_utf8(&mut slice);
389 }
390 self.length = new_length as u8;
391
392 self.assert_sanity();
393 Ok(())
394 }
395
396 #[inline]
407 pub fn as_bytes(&self) -> &[u8] {
408 self.assert_sanity();
409 &self.bytes[0..self.len()]
410 }
411
412 #[inline]
429 pub fn truncate(&mut self, new_len: usize) {
430 self.assert_sanity();
431
432 assert!(
433 self.is_char_boundary(new_len),
434 "inlinable_string::InlineString::truncate: new_len is not a character
435 boundary"
436 );
437 assert!(new_len <= self.len());
438
439 self.length = new_len as u8;
440 self.assert_sanity();
441 }
442
443 #[inline]
458 pub fn pop(&mut self) -> Option<char> {
459 self.assert_sanity();
460
461 match self.char_indices().rev().next() {
462 None => None,
463 Some((idx, ch)) => {
464 self.length = idx as u8;
465 self.assert_sanity();
466 Some(ch)
467 }
468 }
469 }
470
471 #[inline]
490 pub fn remove(&mut self, idx: usize) -> char {
491 self.assert_sanity();
492 assert!(idx < self.len());
493
494 let ch = self
495 .get(idx..)
496 .expect(
497 "inlinable_string::InlineString::remove: idx does not lie on a
498 character boundary",
499 )
500 .chars()
501 .next()
502 .expect("Should be `Some` because `idx < self.len()`");
503 let char_len = ch.len_utf8();
504 let next = idx + char_len;
505
506 unsafe {
507 let ptr = self.bytes.as_mut_ptr();
508
509 ptr::copy(
510 ptr.add(next),
511 ptr.add(idx),
512 self.len() - next,
513 );
514 }
515 self.length -= char_len as u8;
516
517 self.assert_sanity();
518 ch
519 }
520
521 unsafe fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) -> Result<(), NotEnoughSpaceError> {
523 let len = self.len();
524 let amt = bytes.len();
525
526 if amt > INLINE_STRING_CAPACITY - len {
528 return Err(NotEnoughSpaceError);
529 }
530
531 let ptr = self.bytes.as_mut_ptr().add(idx);
532
533 ptr::copy(
535 ptr,
536 ptr.add(amt),
537 len - idx,
538 );
539 ptr::copy(bytes.as_ptr(), self.bytes.as_mut_ptr().add(idx), amt);
541 self.length += amt as u8;
543
544 Ok(())
545 }
546
547 #[inline]
564 pub fn insert(&mut self, idx: usize, ch: char) -> Result<(), NotEnoughSpaceError> {
565 self.assert_sanity();
566 assert!(idx <= self.len());
567
568 let mut bits = [0; 4];
569 let bits = ch.encode_utf8(&mut bits).as_bytes();
570
571 unsafe {
572 self.insert_bytes(idx, bits)?;
573 }
574
575 self.assert_sanity();
576 Ok(())
577 }
578
579 #[inline]
591 pub fn insert_str(&mut self, idx: usize, string: &str) -> Result<(), NotEnoughSpaceError> {
592 self.assert_sanity();
593 assert!(idx <= self.len());
594
595 unsafe {
596 self.insert_bytes(idx, string.as_bytes())?;
597 }
598
599 self.assert_sanity();
600 Ok(())
601 }
602
603 #[inline]
624 pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
625 self.assert_sanity();
626 &mut self.bytes[0..self.length as usize]
627 }
628
629 #[inline]
640 pub fn len(&self) -> usize {
641 self.assert_sanity();
642 self.length as usize
643 }
644
645 #[inline]
658 pub fn is_empty(&self) -> bool {
659 self.assert_sanity();
660 self.length == 0
661 }
662
663 #[inline]
675 pub fn clear(&mut self) {
676 self.assert_sanity();
677 self.length = 0;
678 self.assert_sanity();
679 }
680}
681
682#[cfg(test)]
683mod tests {
684 use alloc::string::String;
685 use super::{InlineString, NotEnoughSpaceError, INLINE_STRING_CAPACITY};
686
687 #[test]
688 fn test_push_str() {
689 let mut s = InlineString::new();
690 assert!(s.push_str("small").is_ok());
691 assert_eq!(s, "small");
692
693 let long_str = "this is a really long string that is much larger than
694 INLINE_STRING_CAPACITY and so cannot be stored inline.";
695 assert_eq!(s.push_str(long_str), Err(NotEnoughSpaceError));
696 assert_eq!(s, "small");
697 }
698
699 #[test]
700 fn test_push() {
701 let mut s = InlineString::new();
702
703 for _ in 0..INLINE_STRING_CAPACITY {
704 assert!(s.push('a').is_ok());
705 }
706
707 assert_eq!(s.push('a'), Err(NotEnoughSpaceError));
708 }
709
710 #[test]
711 fn test_insert() {
712 let mut s = InlineString::new();
713
714 for _ in 0..INLINE_STRING_CAPACITY {
715 assert!(s.insert(0, 'a').is_ok());
716 }
717
718 assert_eq!(s.insert(0, 'a'), Err(NotEnoughSpaceError));
719 }
720
721 #[test]
722 fn test_write() {
723 use core::fmt::{Error, Write};
724
725 let mut s = InlineString::new();
726 let mut normal_string = String::new();
727
728 for _ in 0..INLINE_STRING_CAPACITY {
729 assert!(write!(&mut s, "a").is_ok());
730 assert!(write!(&mut normal_string, "a").is_ok());
731 }
732
733 assert_eq!(write!(&mut s, "a"), Err(Error));
734 assert_eq!(&normal_string[..], &s[..]);
735 }
736}
737
738#[cfg(test)]
739#[cfg(feature = "nightly")]
740mod benches {
741 use test::Bencher;
742
743 #[bench]
744 fn its_fast(_b: &mut Bencher) {}
745}