inlinable_string/
lib.rs

1// Copyright 2015, The inlinable_string crate Developers. See the COPYRIGHT file
2// at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
6// or http://opensource.org/licenses/MIT>, at your option. This file may not be
7// copied, modified, or distributed except according to those terms.
8
9//! The `inlinable_string` crate provides the
10//! [`InlinableString`](./enum.InlinableString.html) type &mdash; an owned,
11//! grow-able UTF-8 string that stores small strings inline and avoids
12//! heap-allocation &mdash; and the
13//! [`StringExt`](./string_ext/trait.StringExt.html) trait which abstracts
14//! string operations over both `std::string::String` and `InlinableString` (or
15//! even your own custom string type).
16//!
17//! `StringExt`'s API is mostly identical to `std::string::String`; unstable and
18//! deprecated methods are not included. A `StringExt` implementation is
19//! provided for both `std::string::String` and `InlinableString`. This enables
20//! `InlinableString` to generally work as a drop-in replacement for
21//! `std::string::String` and `&StringExt` to work with references to either
22//! type.
23//!
24//! # Examples
25//!
26//! ```
27//! use inlinable_string::{InlinableString, StringExt};
28//!
29//! // Small strings are stored inline and don't perform heap-allocation.
30//! let mut s = InlinableString::from("small");
31//! assert_eq!(s.capacity(), inlinable_string::INLINE_STRING_CAPACITY);
32//!
33//! // Inline strings are transparently promoted to heap-allocated strings when
34//! // they grow too big.
35//! s.push_str("a really long string that's bigger than `INLINE_STRING_CAPACITY`");
36//! assert!(s.capacity() > inlinable_string::INLINE_STRING_CAPACITY);
37//!
38//! // This method can work on strings potentially stored inline on the stack,
39//! // on the heap, or plain old `std::string::String`s!
40//! fn takes_a_string_reference(string: &mut StringExt) {
41//!    // Do something with the string...
42//!    string.push_str("it works!");
43//! }
44//!
45//! let mut s1 = String::from("this is a plain std::string::String");
46//! let mut s2 = InlinableString::from("inline");
47//!
48//! // Both work!
49//! takes_a_string_reference(&mut s1);
50//! takes_a_string_reference(&mut s2);
51//! ```
52//!
53//! # Porting Your Code
54//!
55//! * If `my_string` is always on the stack: `let my_string = String::new();` →
56//! `let my_string = InlinableString::new();`
57//!
58//! * `fn foo(string: &mut String) { ... }` → `fn foo(string: &mut StringExt) { ... }`
59//!
60//! * `fn foo(string: &str) { ... }` does not need to be modified.
61//!
62//! * `struct S { member: String }` is a little trickier. If `S` is always stack
63//! allocated, it probably makes sense to make `member` be of type
64//! `InlinableString`. If `S` is heap-allocated and `member` is *always* small,
65//! consider using the more restrictive
66//! [`InlineString`](./inline_string/struct.InlineString.html) type. If `member` is
67//! not always small, then it should probably be left as a `String`.
68//!
69//! # Serialization
70//!
71//! `InlinableString` implements [`serde`][serde-docs]'s `Serialize` and `Deserialize` traits.
72//! Add the `serde` feature to your `Cargo.toml` to enable serialization.
73//!
74//! [serde-docs]: https://serde.rs
75
76#![forbid(missing_docs)]
77#![cfg_attr(feature = "nightly", feature(plugin))]
78#![cfg_attr(all(test, feature = "nightly"), feature(test))]
79#![cfg_attr(feature = "no_std", no_std)]
80
81#[allow(unused_imports)]
82#[cfg_attr(feature = "no_std", macro_use)]
83extern crate alloc;
84
85#[cfg(test)]
86#[cfg(feature = "nightly")]
87extern crate test;
88
89#[cfg(feature = "serde")]
90mod serde_impl;
91
92pub mod inline_string;
93pub mod string_ext;
94
95pub use inline_string::{InlineString, INLINE_STRING_CAPACITY};
96pub use string_ext::StringExt;
97
98use alloc::borrow::{Borrow, Cow};
99use alloc::vec::Vec;
100use alloc::string::{FromUtf16Error, FromUtf8Error, String};
101use core::cmp::Ordering;
102use core::convert;
103use core::fmt;
104use core::hash;
105use core::iter;
106use core::mem;
107use core::ops;
108use core::str::FromStr;
109
110/// An owned, grow-able UTF-8 string that allocates short strings inline on the
111/// stack.
112///
113/// See the [module level documentation](./index.html) for more.
114#[derive(Clone, Eq)]
115pub enum InlinableString {
116    /// A heap-allocated string.
117    Heap(String),
118    /// A small string stored inline.
119    Inline(InlineString),
120}
121
122impl fmt::Debug for InlinableString {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        fmt::Debug::fmt(&self as &str, f)
125    }
126}
127
128impl iter::FromIterator<char> for InlinableString {
129    fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> InlinableString {
130        let mut buf = InlinableString::new();
131        buf.extend(iter);
132        buf
133    }
134}
135
136impl<'a> iter::FromIterator<&'a str> for InlinableString {
137    fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> InlinableString {
138        let mut buf = InlinableString::new();
139        buf.extend(iter);
140        buf
141    }
142}
143
144impl Extend<char> for InlinableString {
145    fn extend<I: IntoIterator<Item = char>>(&mut self, iterable: I) {
146        let iterator = iterable.into_iter();
147        let (lower_bound, _) = iterator.size_hint();
148        self.reserve(lower_bound);
149        for ch in iterator {
150            self.push(ch);
151        }
152    }
153}
154
155impl<'a> Extend<&'a char> for InlinableString {
156    fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
157        self.extend(iter.into_iter().cloned());
158    }
159}
160
161impl<'a> Extend<&'a str> for InlinableString {
162    fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iterable: I) {
163        let iterator = iterable.into_iter();
164        let (lower_bound, _) = iterator.size_hint();
165        self.reserve(lower_bound);
166        for s in iterator {
167            self.push_str(s);
168        }
169    }
170}
171
172impl<'a> ops::Add<&'a str> for InlinableString {
173    type Output = InlinableString;
174
175    #[inline]
176    fn add(mut self, other: &str) -> InlinableString {
177        self.push_str(other);
178        self
179    }
180}
181
182impl PartialOrd<InlinableString> for InlinableString {
183    fn partial_cmp(&self, rhs: &InlinableString) -> Option<Ordering> {
184        Some(Ord::cmp(&self[..], &rhs[..]))
185    }
186}
187
188impl Ord for InlinableString {
189    #[inline]
190    fn cmp(&self, rhs: &InlinableString) -> Ordering {
191        Ord::cmp(&self[..], &rhs[..])
192    }
193}
194
195impl hash::Hash for InlinableString {
196    #[inline]
197    fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
198        (**self).hash(hasher)
199    }
200}
201
202impl Borrow<str> for InlinableString {
203    fn borrow(&self) -> &str {
204        &*self
205    }
206}
207
208impl AsRef<str> for InlinableString {
209    fn as_ref(&self) -> &str {
210        match *self {
211            InlinableString::Heap(ref s) => &*s,
212            InlinableString::Inline(ref s) => &*s,
213        }
214    }
215}
216
217impl AsMut<str> for InlinableString {
218    fn as_mut(&mut self) -> &mut str {
219        match *self {
220            InlinableString::Heap(ref mut s) => s.as_mut_str(),
221            InlinableString::Inline(ref mut s) => &mut s[..],
222        }
223    }
224}
225
226impl<'a> From<&'a str> for InlinableString {
227    #[inline]
228    fn from(string: &'a str) -> InlinableString {
229        if string.len() <= INLINE_STRING_CAPACITY {
230            InlinableString::Inline(string.into())
231        } else {
232            InlinableString::Heap(string.into())
233        }
234    }
235}
236
237impl From<String> for InlinableString {
238    #[inline]
239    fn from(string: String) -> InlinableString {
240        if string.len() <= INLINE_STRING_CAPACITY {
241            InlinableString::Inline(string.as_str().into())
242        } else {
243            InlinableString::Heap(string)
244        }
245    }
246}
247
248impl FromStr for InlinableString {
249    type Err = convert::Infallible;
250
251    #[inline]
252    fn from_str(s: &str) -> Result<InlinableString, convert::Infallible> {
253        Ok(InlinableString::from(s))
254    }
255}
256
257impl Default for InlinableString {
258    fn default() -> Self {
259        InlinableString::new()
260    }
261}
262
263impl fmt::Display for InlinableString {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
265        match *self {
266            InlinableString::Heap(ref s) => s.fmt(f),
267            InlinableString::Inline(ref s) => s.fmt(f),
268        }
269    }
270}
271
272impl fmt::Write for InlinableString {
273    fn write_char(&mut self, ch: char) -> Result<(), fmt::Error> {
274        self.push(ch);
275        Ok(())
276    }
277    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
278        self.push_str(s);
279        Ok(())
280    }
281}
282
283impl ops::Index<ops::Range<usize>> for InlinableString {
284    type Output = str;
285
286    #[inline]
287    fn index(&self, index: ops::Range<usize>) -> &str {
288        match *self {
289            InlinableString::Heap(ref s) => s.index(index),
290            InlinableString::Inline(ref s) => s.index(index),
291        }
292    }
293}
294
295impl ops::Index<ops::RangeTo<usize>> for InlinableString {
296    type Output = str;
297
298    #[inline]
299    fn index(&self, index: ops::RangeTo<usize>) -> &str {
300        match *self {
301            InlinableString::Heap(ref s) => s.index(index),
302            InlinableString::Inline(ref s) => s.index(index),
303        }
304    }
305}
306
307impl ops::Index<ops::RangeFrom<usize>> for InlinableString {
308    type Output = str;
309
310    #[inline]
311    fn index(&self, index: ops::RangeFrom<usize>) -> &str {
312        match *self {
313            InlinableString::Heap(ref s) => s.index(index),
314            InlinableString::Inline(ref s) => s.index(index),
315        }
316    }
317}
318
319impl ops::Index<ops::RangeFull> for InlinableString {
320    type Output = str;
321
322    #[inline]
323    fn index(&self, index: ops::RangeFull) -> &str {
324        match *self {
325            InlinableString::Heap(ref s) => s.index(index),
326            InlinableString::Inline(ref s) => s.index(index),
327        }
328    }
329}
330
331impl ops::IndexMut<ops::Range<usize>> for InlinableString {
332    #[inline]
333    fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
334        match *self {
335            InlinableString::Heap(ref mut s) => s.index_mut(index),
336            InlinableString::Inline(ref mut s) => s.index_mut(index),
337        }
338    }
339}
340
341impl ops::IndexMut<ops::RangeTo<usize>> for InlinableString {
342    #[inline]
343    fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
344        match *self {
345            InlinableString::Heap(ref mut s) => s.index_mut(index),
346            InlinableString::Inline(ref mut s) => s.index_mut(index),
347        }
348    }
349}
350
351impl ops::IndexMut<ops::RangeFrom<usize>> for InlinableString {
352    #[inline]
353    fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
354        match *self {
355            InlinableString::Heap(ref mut s) => s.index_mut(index),
356            InlinableString::Inline(ref mut s) => s.index_mut(index),
357        }
358    }
359}
360
361impl ops::IndexMut<ops::RangeFull> for InlinableString {
362    #[inline]
363    fn index_mut(&mut self, index: ops::RangeFull) -> &mut str {
364        match *self {
365            InlinableString::Heap(ref mut s) => s.index_mut(index),
366            InlinableString::Inline(ref mut s) => s.index_mut(index),
367        }
368    }
369}
370
371impl ops::Deref for InlinableString {
372    type Target = str;
373
374    #[inline]
375    fn deref(&self) -> &str {
376        match *self {
377            InlinableString::Heap(ref s) => s.deref(),
378            InlinableString::Inline(ref s) => s.deref(),
379        }
380    }
381}
382
383impl ops::DerefMut for InlinableString {
384    #[inline]
385    fn deref_mut(&mut self) -> &mut str {
386        match *self {
387            InlinableString::Heap(ref mut s) => s.deref_mut(),
388            InlinableString::Inline(ref mut s) => s.deref_mut(),
389        }
390    }
391}
392
393impl PartialEq<InlinableString> for InlinableString {
394    #[inline]
395    fn eq(&self, rhs: &InlinableString) -> bool {
396        PartialEq::eq(&self[..], &rhs[..])
397    }
398}
399
400macro_rules! impl_eq {
401    ($lhs:ty, $rhs: ty) => {
402        impl<'a> PartialEq<$rhs> for $lhs {
403            #[inline]
404            fn eq(&self, other: &$rhs) -> bool {
405                PartialEq::eq(&self[..], &other[..])
406            }
407        }
408
409        impl<'a> PartialEq<$lhs> for $rhs {
410            #[inline]
411            fn eq(&self, other: &$lhs) -> bool {
412                PartialEq::eq(&self[..], &other[..])
413            }
414        }
415    };
416}
417
418impl_eq! { InlinableString, str }
419impl_eq! { InlinableString, String }
420impl_eq! { InlinableString, &'a str }
421impl_eq! { InlinableString, InlineString }
422impl_eq! { Cow<'a, str>, InlinableString }
423
424impl<'a> StringExt<'a> for InlinableString {
425    #[inline]
426    fn new() -> Self {
427        InlinableString::Inline(InlineString::new())
428    }
429
430    #[inline]
431    fn with_capacity(capacity: usize) -> Self {
432        if capacity <= INLINE_STRING_CAPACITY {
433            InlinableString::Inline(InlineString::new())
434        } else {
435            InlinableString::Heap(String::with_capacity(capacity))
436        }
437    }
438
439    #[inline]
440    fn from_utf8(vec: Vec<u8>) -> Result<Self, FromUtf8Error> {
441        String::from_utf8(vec).map(InlinableString::Heap)
442    }
443
444    #[inline]
445    fn from_utf16(v: &[u16]) -> Result<Self, FromUtf16Error> {
446        String::from_utf16(v).map(InlinableString::Heap)
447    }
448
449    #[inline]
450    fn from_utf16_lossy(v: &[u16]) -> Self {
451        InlinableString::Heap(String::from_utf16_lossy(v))
452    }
453
454    #[inline]
455    unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> Self {
456        InlinableString::Heap(String::from_raw_parts(buf, length, capacity))
457    }
458
459    #[inline]
460    unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> Self {
461        InlinableString::Heap(String::from_utf8_unchecked(bytes))
462    }
463
464    #[inline]
465    fn into_bytes(self) -> Vec<u8> {
466        match self {
467            InlinableString::Heap(s) => s.into_bytes(),
468            InlinableString::Inline(s) => Vec::from(&s[..]),
469        }
470    }
471
472    #[inline]
473    fn push_str(&mut self, string: &str) {
474        let promoted = match *self {
475            InlinableString::Inline(ref mut s) => {
476                if s.push_str(string).is_ok() {
477                    return;
478                }
479                let mut promoted = String::with_capacity(string.len() + s.len());
480                promoted.push_str(&*s);
481                promoted.push_str(string);
482                promoted
483            }
484            InlinableString::Heap(ref mut s) => {
485                s.push_str(string);
486                return;
487            }
488        };
489        mem::swap(self, &mut InlinableString::Heap(promoted));
490    }
491
492    #[inline]
493    fn capacity(&self) -> usize {
494        match *self {
495            InlinableString::Heap(ref s) => s.capacity(),
496            InlinableString::Inline(_) => INLINE_STRING_CAPACITY,
497        }
498    }
499
500    #[inline]
501    fn reserve(&mut self, additional: usize) {
502        let promoted = match *self {
503            InlinableString::Inline(ref s) => {
504                let new_capacity = s.len() + additional;
505                if new_capacity <= INLINE_STRING_CAPACITY {
506                    return;
507                }
508                let mut promoted = String::with_capacity(new_capacity);
509                promoted.push_str(&s);
510                promoted
511            }
512            InlinableString::Heap(ref mut s) => {
513                s.reserve(additional);
514                return;
515            }
516        };
517        mem::swap(self, &mut InlinableString::Heap(promoted));
518    }
519
520    #[inline]
521    fn reserve_exact(&mut self, additional: usize) {
522        let promoted = match *self {
523            InlinableString::Inline(ref s) => {
524                let new_capacity = s.len() + additional;
525                if new_capacity <= INLINE_STRING_CAPACITY {
526                    return;
527                }
528                let mut promoted = String::with_capacity(new_capacity);
529                promoted.push_str(&s);
530                promoted
531            }
532            InlinableString::Heap(ref mut s) => {
533                s.reserve_exact(additional);
534                return;
535            }
536        };
537        mem::swap(self, &mut InlinableString::Heap(promoted));
538    }
539
540    #[inline]
541    fn shrink_to_fit(&mut self) {
542        if self.len() <= INLINE_STRING_CAPACITY {
543            let demoted = if let InlinableString::Heap(ref s) = *self {
544                InlineString::from(&s[..])
545            } else {
546                return;
547            };
548            mem::swap(self, &mut InlinableString::Inline(demoted));
549            return;
550        }
551
552        match *self {
553            InlinableString::Heap(ref mut s) => s.shrink_to_fit(),
554            _ => panic!("inlinable_string: internal error: this branch should be unreachable"),
555        };
556    }
557
558    #[inline]
559    fn push(&mut self, ch: char) {
560        let promoted = match *self {
561            InlinableString::Inline(ref mut s) => {
562                if s.push(ch).is_ok() {
563                    return;
564                }
565
566                let mut promoted = String::with_capacity(s.len() + 1);
567                promoted.push_str(&*s);
568                promoted.push(ch);
569                promoted
570            }
571            InlinableString::Heap(ref mut s) => {
572                s.push(ch);
573                return;
574            }
575        };
576
577        mem::swap(self, &mut InlinableString::Heap(promoted));
578    }
579
580    #[inline]
581    fn as_bytes(&self) -> &[u8] {
582        match *self {
583            InlinableString::Heap(ref s) => s.as_bytes(),
584            InlinableString::Inline(ref s) => s.as_bytes(),
585        }
586    }
587
588    #[inline]
589    fn truncate(&mut self, new_len: usize) {
590        match *self {
591            InlinableString::Heap(ref mut s) => s.truncate(new_len),
592            InlinableString::Inline(ref mut s) => s.truncate(new_len),
593        };
594    }
595
596    #[inline]
597    fn pop(&mut self) -> Option<char> {
598        match *self {
599            InlinableString::Heap(ref mut s) => s.pop(),
600            InlinableString::Inline(ref mut s) => s.pop(),
601        }
602    }
603
604    #[inline]
605    fn remove(&mut self, idx: usize) -> char {
606        match *self {
607            InlinableString::Heap(ref mut s) => s.remove(idx),
608            InlinableString::Inline(ref mut s) => s.remove(idx),
609        }
610    }
611
612    #[inline]
613    fn insert(&mut self, idx: usize, ch: char) {
614        let promoted = match *self {
615            InlinableString::Heap(ref mut s) => {
616                s.insert(idx, ch);
617                return;
618            }
619            InlinableString::Inline(ref mut s) => {
620                if s.insert(idx, ch).is_ok() {
621                    return;
622                }
623
624                let mut promoted = String::with_capacity(s.len() + 1);
625                promoted.push_str(&s[..idx]);
626                promoted.push(ch);
627                promoted.push_str(&s[idx..]);
628                promoted
629            }
630        };
631
632        mem::swap(self, &mut InlinableString::Heap(promoted));
633    }
634
635    #[inline]
636    fn insert_str(&mut self, idx: usize, string: &str) {
637        let promoted = match *self {
638            InlinableString::Heap(ref mut s) => {
639                s.insert_str(idx, string);
640                return;
641            }
642            InlinableString::Inline(ref mut s) => {
643                if s.insert_str(idx, string).is_ok() {
644                    return;
645                }
646
647                let mut promoted = String::with_capacity(s.len() + string.len());
648                promoted.push_str(&s[..idx]);
649                promoted.push_str(string);
650                promoted.push_str(&s[idx..]);
651                promoted
652            }
653        };
654
655        mem::swap(self, &mut InlinableString::Heap(promoted));
656    }
657
658    #[inline]
659    unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
660        match *self {
661            InlinableString::Heap(ref mut s) => &mut s.as_mut_vec()[..],
662            InlinableString::Inline(ref mut s) => s.as_mut_slice(),
663        }
664    }
665
666    #[inline]
667    fn len(&self) -> usize {
668        match *self {
669            InlinableString::Heap(ref s) => s.len(),
670            InlinableString::Inline(ref s) => s.len(),
671        }
672    }
673}
674
675#[cfg(test)]
676mod tests {
677    use alloc::string::{String, ToString};
678    use core::iter::FromIterator;
679    use super::{InlinableString, StringExt, INLINE_STRING_CAPACITY};
680    use core::cmp::Ordering;
681    use core::str::FromStr;
682
683    #[test]
684    fn test_size() {
685        use core::mem::size_of;
686        assert_eq!(size_of::<InlinableString>(), 4 * size_of::<usize>());
687    }
688
689    // First, specifically test operations that overflow InlineString's capacity
690    // and require promoting the string to heap allocation.
691
692    #[test]
693    fn test_push_str() {
694        let mut s = InlinableString::new();
695        s.push_str("small");
696        assert_eq!(s, "small");
697
698        let long_str = "this is a really long string that is much larger than
699                        INLINE_STRING_CAPACITY and so cannot be stored inline.";
700        s.push_str(long_str);
701        assert_eq!(s, String::from("small") + long_str);
702    }
703
704    #[test]
705    fn test_write() {
706        use core::fmt::Write;
707        let mut s = InlinableString::new();
708        write!(&mut s, "small").expect("!write");
709        assert_eq!(s, "small");
710
711        let long_str = "this is a really long string that is much larger than
712                        INLINE_STRING_CAPACITY and so cannot be stored inline.";
713        write!(&mut s, "{}", long_str).expect("!write");
714        assert_eq!(s, String::from("small") + long_str);
715    }
716
717    #[test]
718    fn test_push() {
719        let mut s = InlinableString::new();
720
721        for _ in 0..INLINE_STRING_CAPACITY {
722            s.push('a');
723        }
724        s.push('a');
725
726        assert_eq!(
727            s,
728            String::from_iter((0..INLINE_STRING_CAPACITY + 1).map(|_| 'a'))
729        );
730    }
731
732    #[test]
733    fn test_insert() {
734        let mut s = InlinableString::new();
735
736        for _ in 0..INLINE_STRING_CAPACITY {
737            s.insert(0, 'a');
738        }
739        s.insert(0, 'a');
740
741        assert_eq!(
742            s,
743            String::from_iter((0..INLINE_STRING_CAPACITY + 1).map(|_| 'a'))
744        );
745    }
746
747    #[test]
748    fn test_insert_str() {
749        let mut s = InlinableString::new();
750
751        for _ in 0..(INLINE_STRING_CAPACITY / 3) {
752            s.insert_str(0, "foo");
753        }
754        s.insert_str(0, "foo");
755
756        assert_eq!(
757            s,
758            String::from_iter((0..(INLINE_STRING_CAPACITY / 3) + 1).map(|_| "foo"))
759        );
760    }
761
762    // Next, some general sanity tests.
763
764    #[test]
765    fn test_new() {
766        let s = <InlinableString as StringExt>::new();
767        assert!(StringExt::is_empty(&s));
768    }
769
770    #[test]
771    fn test_with_capacity() {
772        let s = <InlinableString as StringExt>::with_capacity(10);
773        assert!(StringExt::capacity(&s) >= 10);
774    }
775
776    #[test]
777    fn test_from_utf8() {
778        let s = <InlinableString as StringExt>::from_utf8(vec![104, 101, 108, 108, 111]);
779        assert_eq!(s.unwrap(), "hello");
780    }
781
782    #[test]
783    fn test_from_utf16() {
784        let v = &mut [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
785        let s = <InlinableString as StringExt>::from_utf16(v);
786        assert_eq!(s.unwrap(), "𝄞music");
787    }
788
789    #[test]
790    fn test_from_utf16_lossy() {
791        let input = b"Hello \xF0\x90\x80World";
792        let output = <InlinableString as StringExt>::from_utf8_lossy(input);
793        assert_eq!(output, "Hello \u{FFFD}World");
794    }
795
796    #[test]
797    fn test_into_bytes() {
798        let s = InlinableString::from("hello");
799        let bytes = StringExt::into_bytes(s);
800        assert_eq!(bytes, [104, 101, 108, 108, 111]);
801    }
802
803    #[test]
804    fn test_capacity() {
805        let s = <InlinableString as StringExt>::with_capacity(100);
806        assert!(InlinableString::capacity(&s) >= 100);
807    }
808
809    #[test]
810    fn test_reserve() {
811        let mut s = <InlinableString as StringExt>::new();
812        StringExt::reserve(&mut s, 100);
813        assert!(InlinableString::capacity(&s) >= 100);
814    }
815
816    #[test]
817    fn test_reserve_exact() {
818        let mut s = <InlinableString as StringExt>::new();
819        StringExt::reserve_exact(&mut s, 100);
820        assert!(InlinableString::capacity(&s) >= 100);
821    }
822
823    #[test]
824    fn test_shrink_to_fit() {
825        let mut s = <InlinableString as StringExt>::with_capacity(100);
826        StringExt::push_str(&mut s, "foo");
827        StringExt::shrink_to_fit(&mut s);
828        assert_eq!(InlinableString::capacity(&s), INLINE_STRING_CAPACITY);
829    }
830
831    #[test]
832    fn test_truncate() {
833        let mut s = InlinableString::from("foo");
834        StringExt::truncate(&mut s, 1);
835        assert_eq!(s, "f");
836    }
837
838    #[test]
839    fn test_pop() {
840        let mut s = InlinableString::from("foo");
841        assert_eq!(StringExt::pop(&mut s), Some('o'));
842        assert_eq!(StringExt::pop(&mut s), Some('o'));
843        assert_eq!(StringExt::pop(&mut s), Some('f'));
844        assert_eq!(StringExt::pop(&mut s), None);
845    }
846
847    #[test]
848    fn test_ord() {
849        let s1 = InlinableString::from("foo");
850        let s2 = InlinableString::from("bar");
851        assert_eq!(Ord::cmp(&s1, &s2), Ordering::Greater);
852        assert_eq!(Ord::cmp(&s1, &s1), Ordering::Equal);
853    }
854
855    #[test]
856    fn test_display() {
857        let short = InlinableString::from("he");
858        let long = InlinableString::from("hello world");
859        assert_eq!(format!("{}", short), "he".to_string());
860        assert_eq!(format!("{}", long), "hello world".to_string());
861    }
862
863    #[test]
864    fn test_debug() {
865        let short = InlinableString::from("he");
866        let long = InlinableString::from("hello world hello world hello world");
867        assert_eq!(format!("{:?}", short), "\"he\"");
868        assert_eq!(
869            format!("{:?}", long),
870            "\"hello world hello world hello world\""
871        );
872    }
873
874    // example generic function where impl FromStr for InlinableString is useful
875    fn parse_non_empty<T: FromStr>(s: &str) -> Option<T> {
876        if s.len() == 0 {
877            None
878        } else {
879            let val = T::from_str(s).unwrap_or_else(|_| panic!("unwrap"));
880            Some(val)
881        }
882    }
883
884    #[test]
885    fn test_fromstr() {
886        assert_eq!(parse_non_empty::<InlinableString>(""), None);
887        assert_eq!(parse_non_empty::<u8>("10"), Some(10u8));
888        assert_eq!(
889            parse_non_empty::<InlinableString>("foo"),
890            Some(InlinableString::from("foo"))
891        );
892    }
893}
894
895#[cfg(test)]
896#[cfg(feature = "nightly")]
897mod benches {
898    #[cfg(feature = "no_std")]
899    use alloc::string::String;
900    use super::{InlinableString, StringExt};
901    use test::{black_box, Bencher};
902
903    const SMALL_STR: &'static str = "foobar";
904
905    const LARGE_STR: &'static str =
906        "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
907         abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
908         abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
909         abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
910         abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
911         abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
912         abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
913         abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
914
915    #[bench]
916    fn bench_std_string_push_str_small_onto_empty(b: &mut Bencher) {
917        b.iter(|| {
918            let mut s = String::new();
919            s.push_str(SMALL_STR);
920            black_box(s);
921        });
922    }
923
924    #[bench]
925    fn bench_inlinable_string_push_str_small_onto_empty(b: &mut Bencher) {
926        b.iter(|| {
927            let mut s = InlinableString::new();
928            s.push_str(SMALL_STR);
929            black_box(s);
930        });
931    }
932
933    #[bench]
934    fn bench_std_string_push_str_large_onto_empty(b: &mut Bencher) {
935        b.iter(|| {
936            let mut s = String::new();
937            s.push_str(LARGE_STR);
938            black_box(s);
939        });
940    }
941
942    #[bench]
943    fn bench_inlinable_string_push_str_large_onto_empty(b: &mut Bencher) {
944        b.iter(|| {
945            let mut s = InlinableString::new();
946            s.push_str(LARGE_STR);
947            black_box(s);
948        });
949    }
950
951    #[bench]
952    fn bench_std_string_push_str_small_onto_small(b: &mut Bencher) {
953        b.iter(|| {
954            let mut s = String::from(SMALL_STR);
955            s.push_str(SMALL_STR);
956            black_box(s);
957        });
958    }
959
960    #[bench]
961    fn bench_inlinable_string_push_str_small_onto_small(b: &mut Bencher) {
962        b.iter(|| {
963            let mut s = InlinableString::from(SMALL_STR);
964            s.push_str(SMALL_STR);
965            black_box(s);
966        });
967    }
968
969    #[bench]
970    fn bench_std_string_push_str_large_onto_large(b: &mut Bencher) {
971        b.iter(|| {
972            let mut s = String::from(LARGE_STR);
973            s.push_str(LARGE_STR);
974            black_box(s);
975        });
976    }
977
978    #[bench]
979    fn bench_inlinable_string_push_str_large_onto_large(b: &mut Bencher) {
980        b.iter(|| {
981            let mut s = InlinableString::from(LARGE_STR);
982            s.push_str(LARGE_STR);
983            black_box(s);
984        });
985    }
986
987    #[bench]
988    fn bench_std_string_from_small(b: &mut Bencher) {
989        b.iter(|| {
990            let s = String::from(SMALL_STR);
991            black_box(s);
992        });
993    }
994
995    #[bench]
996    fn bench_inlinable_string_from_small(b: &mut Bencher) {
997        b.iter(|| {
998            let s = InlinableString::from(SMALL_STR);
999            black_box(s);
1000        });
1001    }
1002
1003    #[bench]
1004    fn bench_std_string_from_large(b: &mut Bencher) {
1005        b.iter(|| {
1006            let s = String::from(LARGE_STR);
1007            black_box(s);
1008        });
1009    }
1010
1011    #[bench]
1012    fn bench_inlinable_string_from_large(b: &mut Bencher) {
1013        b.iter(|| {
1014            let s = InlinableString::from(LARGE_STR);
1015            black_box(s);
1016        });
1017    }
1018}