arraystring/
lib.rs

1//! Fixed capacity stack based generic string
2//!
3//! Since rust doesn't have constant generics yet `typenum` is used to allow for generic arrays (`U1` to `U255`)
4//!
5//! Can't outgrow initial capacity (defined at compile time), always occupies [`capacity`] `+ 1` bytes of memory
6//!
7//! *Doesn't allocate memory on the heap and never panics in release (all panic branches are stripped at compile time - except `Index`/`IndexMut` traits, since they are supposed to)*
8//!
9//! ## Why
10//!
11//! Data is generally bounded, you don't want a phone number with 30 characters, nor a username with 100. You probably don't even support it in your database.
12//!
13//! Why pay the cost of heap allocations of strings with unlimited capacity if you have limited boundaries?
14//!
15//! Stack based strings are generally faster to create, clone and append to than heap based strings (custom allocators and thread-locals may help with heap based ones).
16//!
17//! But that becomes less true as you increase the array size, 255 bytes is the maximum we accept (bigger will just wrap) and it's probably already slower than heap based strings of that size (like in `std::string::String`)
18//!
19//! There are other stack based strings out there, they generally can have "unlimited" capacity (heap allocate), but the stack based size is defined by the library implementor, we go through a different route by implementing a string based in a generic array.
20//!
21//! Array based strings always occupies the full space in memory, so they may use more memory (in the stack) than dynamic strings.
22//!
23//! [`capacity`]: ./struct.ArrayString.html#method.capacity
24//!
25//! ## Features
26//!
27//! **default:** `std`
28//!
29//! - `std` enabled by default, enables `std` compatibility - `impl Error` trait for errors (remove it to be `#[no_std]` compatible)
30//! - `serde-traits` enables serde traits integration (`Serialize`/`Deserialize`)
31//!
32//!     Opperates like `String`, but truncates it if it's bigger than capacity
33//!
34//!  - `diesel-traits` enables diesel traits integration (`Insertable`/`Queryable`)
35//!
36//!      Opperates like `String`, but truncates it if it's bigger than capacity
37//!
38//! - `logs` enables internal logging
39//!
40//!     You will probably only need this if you are debugging this library
41//!
42//! ## Examples
43//!
44//! ```rust
45//! use arraystring::{Error, ArrayString, typenum::U5, typenum::U20};
46//!
47//! type Username = ArrayString<U20>;
48//! type Role = ArrayString<U5>;
49//!
50//! #[derive(Debug)]
51//! pub struct User {
52//!     pub username: Username,
53//!     pub role: Role,
54//! }
55//!
56//! fn main() -> Result<(), Error> {
57//!     let user = User {
58//!         username: Username::try_from_str("user")?,
59//!         role: Role::try_from_str("admin")?
60//!     };
61//!     println!("{:?}", user);
62//!
63//!     Ok(())
64//! }
65//! ```
66//!
67//!  ## Comparisons
68//! 
69//! *These benchmarks ran while I streamed video and used my computer (with* **non-disclosed specs**) *as usual, so don't take the actual times too seriously, just focus on the comparison*
70//! 
71//! ```my_custom_benchmark
72//! small-string  (23 bytes)      clone                  4.837 ns
73//! small-string  (23 bytes)      try_from_str          14.777 ns
74//! small-string  (23 bytes)      from_str_truncate     11.360 ns
75//! small-string  (23 bytes)      from_str_unchecked    11.291 ns
76//! small-string  (23 bytes)      try_push_str           1.162 ns
77//! small-string  (23 bytes)      push_str               3.490 ns
78//! small-string  (23 bytes)      push_str_unchecked     1.098 ns
79//! -------------------------------------------------------------
80//! cache-string  (63 bytes)      clone                 10.170 ns
81//! cache-string  (63 bytes)      try_from_str          25.579 ns
82//! cache-string  (63 bytes)      from_str_truncate     16.977 ns
83//! cache-string  (63 bytes)      from_str_unchecked    17.201 ns
84//! cache-string  (63 bytes)      try_push_str           1.160 ns
85//! cache-string  (63 bytes)      push_str               3.486 ns
86//! cache-string  (63 bytes)      push_str_unchecked     1.115 ns
87//! -------------------------------------------------------------
88//! max-string   (255 bytes)      clone                147.410 ns
89//! max-string   (255 bytes)      try_from_str         157.340 ns
90//! max-string   (255 bytes)      from_str_truncate    158.000 ns
91//! max-string   (255 bytes)      from_str_unchecked   158.420 ns
92//! max-string   (255 bytes)      try_push_str           1.167 ns
93//! max-string   (255 bytes)      push_str               4.337 ns
94//! max-string   (255 bytes)      push_str_unchecked     1.103 ns
95//! -------------------------------------------------------------
96//! string                        clone                 33.295 ns
97//! string                        from                  32.512 ns
98//! string                        push str              28.128 ns
99//! -------------------------------------------------------------
100//! inlinable-string (30 bytes)   clone                 16.751 ns
101//! inlinable-string (30 bytes)   from_str              29.310 ns
102//! inlinable-string (30 bytes)   push_str               2.865 ns
103//! -------------------------------------------------------------
104//! smallstring crate (20 bytes)  clone                 60.988 ns
105//! smallstring crate (20 bytes)  from_str              50.233 ns
106//! ```
107//!
108//! ## Licenses
109//!
110//! `MIT` and `Apache-2.0`
111
112#![doc(html_root_url = "https://docs.rs/arraystring/0.3.0/arraystring")]
113#![cfg_attr(docs_rs_workaround, feature(doc_cfg))]
114#![cfg_attr(not(feature = "std"), no_std)]
115#![warn(
116    missing_docs,
117    missing_debug_implementations,
118    trivial_numeric_casts,
119    unused_extern_crates,
120    unused_import_braces,
121    unused_qualifications,
122    unused_results,
123    bad_style,
124    const_err,
125    dead_code,
126    improper_ctypes,
127    legacy_directory_ownership,
128    non_shorthand_field_patterns,
129    no_mangle_generic_items,
130    overflowing_literals,
131    path_statements,
132    patterns_in_fns_without_body,
133    plugin_as_library,
134    private_in_public,
135    safe_extern_statics,
136    unconditional_recursion,
137    unions_with_drop_fields,
138    unused_allocation,
139    unused_comparisons,
140    unused_parens,
141    while_true
142)]
143#![doc(test(attr(deny(warnings))))]
144
145pub use typenum;
146
147/// Remove logging macros when they are disabled (at compile time)
148#[macro_use]
149#[cfg(not(feature = "logs"))]
150#[allow(unused)]
151mod mock {
152    macro_rules! trace(($($x:tt)*) => ());
153    macro_rules! debug(($($x:tt)*) => ());
154    macro_rules! info(($($x:tt)*) => ());
155    macro_rules! warn(($($x:tt)*) => ());
156    macro_rules! error(($($x:tt)*) => ());
157}
158
159#[cfg(all(feature = "diesel-traits", test))]
160#[macro_use]
161extern crate diesel;
162
163mod arraystring;
164pub mod drain;
165pub mod error;
166mod generic;
167mod implementations;
168#[cfg(any(feature = "serde-traits", feature = "diesel-traits"))]
169mod integration;
170#[doc(hidden)]
171pub mod utils;
172
173/// Most used traits and data-strucutres
174pub mod prelude {
175    pub use crate::arraystring::ArrayString;
176    pub use crate::drain::Drain;
177    pub use crate::error::{OutOfBounds, Utf16, Utf8};
178    pub use crate::{generic::Capacity, CacheString, MaxString, SmallString};
179}
180
181pub use crate::arraystring::ArrayString;
182pub use crate::error::Error;
183
184use crate::prelude::*;
185use core::fmt::{self, Debug, Display, Formatter, Write};
186use core::{borrow::Borrow, borrow::BorrowMut, ops::*};
187use core::{cmp::Ordering, hash::Hash, hash::Hasher, str::FromStr};
188#[cfg(feature = "logs")]
189use log::trace;
190use typenum::{Unsigned, U255, U63};
191
192#[cfg(target_pointer_width="64")]
193use typenum::U23;
194
195#[cfg(target_pointer_width="32")]
196use typenum::U11;
197
198/// String with the same `mem::size_of` of a `String`
199///
200/// 24 bytes in 64 bits architecture
201///
202/// 12 bytes in 32 bits architecture (or others)
203#[cfg(target_pointer_width="64")]
204pub type SmallString = ArrayString<U23>;
205
206/// String with the same `mem::size_of` of a `String`
207///
208/// 24 bytes in 64 bits architecture
209///
210/// 12 bytes in 32 bits architecture (or others)
211#[cfg(not(target_pointer_width="64"))]
212pub type SmallString = ArrayString<U11>;
213
214/// Biggest array based string (255 bytes of string)
215pub type MaxString = ArrayString<U255>;
216
217/// Newtype string that occupies 64 bytes in memory and is 64 bytes aligned (full cache line)
218///
219/// 63 bytes of string
220#[repr(align(64))]
221#[derive(Copy, Clone, Default)]
222pub struct CacheString(pub ArrayString<U63>);
223
224impl CacheString {
225    /// Creates new empty `CacheString`.
226    ///
227    /// ```rust
228    /// # use arraystring::prelude::*;
229    /// # let _ = env_logger::try_init();
230    /// let string = CacheString::new();
231    /// assert!(string.is_empty());
232    /// ```
233    #[inline]
234    pub fn new() -> Self {
235        trace!("New empty CacheString");
236        Self::default()
237    }
238
239    /// Creates new `CacheString` from string slice if length is lower or equal to [`capacity`], otherwise returns an error.
240    ///
241    /// [`capacity`]: ./struct.CacheString.html#method.capacity
242    /// ```rust
243    /// # use arraystring::{error::Error, prelude::*};
244    /// # fn main() -> Result<(), Error> {
245    /// # let _ = env_logger::try_init();
246    /// let string = CacheString::try_from_str("My String")?;
247    /// assert_eq!(string.as_str(), "My String");
248    ///
249    /// assert_eq!(CacheString::try_from_str("")?.as_str(), "");
250    ///
251    /// let out_of_bounds = "0".repeat(CacheString::capacity() as usize + 1);
252    /// assert!(CacheString::try_from_str(out_of_bounds).is_err());
253    /// # Ok(())
254    /// # }
255    /// ```
256    #[inline]
257    pub fn try_from_str<S>(s: S) -> Result<Self, OutOfBounds>
258    where
259        S: AsRef<str>,
260    {
261        Ok(CacheString(ArrayString::try_from_str(s)?))
262    }
263
264    /// Creates new `CacheString` from string slice truncating size if bigger than [`capacity`].
265    ///
266    /// [`capacity`]: ./struct.CacheString.html#method.capacity
267    ///
268    /// ```rust
269    /// # use arraystring::prelude::*;
270    /// # let _ = env_logger::try_init();
271    /// let string = CacheString::from_str_truncate("My String");
272    /// # assert_eq!(string.as_str(), "My String");
273    /// println!("{}", string);
274    ///
275    /// let truncate = "0".repeat(CacheString::capacity() as usize + 1);
276    /// let truncated = "0".repeat(CacheString::capacity().into());
277    /// let string = CacheString::from_str_truncate(&truncate);
278    /// assert_eq!(string.as_str(), truncated);
279    /// ```
280    #[inline]
281    pub fn from_str_truncate<S>(string: S) -> Self
282    where
283        S: AsRef<str>,
284    {
285        CacheString(ArrayString::from_str_truncate(string))
286    }
287
288    /// Creates new `CacheString` from string slice assuming length is appropriate.
289    ///
290    /// # Safety
291    ///
292    /// It's UB if `string.len()` > [`capacity`].
293    ///
294    /// [`capacity`]: ./struct.CacheString.html#method.capacity
295    ///
296    /// ```rust
297    /// # use arraystring::prelude::*;
298    /// let filled = "0".repeat(CacheString::capacity().into());
299    /// let string = unsafe {
300    ///     CacheString::from_str_unchecked(&filled)
301    /// };
302    /// assert_eq!(string.as_str(), filled.as_str());
303    ///
304    /// // Undefined behavior, don't do it
305    /// // let out_of_bounds = "0".repeat(CacheString::capacity().into() + 1);
306    /// // let ub = unsafe { CacheString::from_str_unchecked(out_of_bounds) };
307    /// ```
308    #[inline]
309    pub unsafe fn from_str_unchecked<S>(string: S) -> Self
310    where
311        S: AsRef<str>,
312    {
313        CacheString(ArrayString::from_str_unchecked(string))
314    }
315
316    /// Creates new `CacheString` from string slice iterator if total length is lower or equal to [`capacity`], otherwise returns an error.
317    ///
318    /// [`capacity`]: ./struct.CacheString.html#method.capacity
319    ///
320    /// ```rust
321    /// # use arraystring::prelude::*;
322    /// # fn main() -> Result<(), OutOfBounds> {
323    /// let string = CacheString::try_from_iterator(&["My String", " My Other String"][..])?;
324    /// assert_eq!(string.as_str(), "My String My Other String");
325    ///
326    /// let out_of_bounds = (0..100).map(|_| "000");
327    /// assert!(CacheString::try_from_iterator(out_of_bounds).is_err());
328    /// # Ok(())
329    /// # }
330    /// ```
331    #[inline]
332    pub fn try_from_iterator<U, I>(iter: I) -> Result<Self, OutOfBounds>
333    where
334        U: AsRef<str>,
335        I: IntoIterator<Item = U>,
336    {
337        Ok(CacheString(ArrayString::try_from_iterator(iter)?))
338    }
339
340    /// Creates new `CacheString` from string slice iterator truncating size if bigger than [`capacity`].
341    ///
342    /// [`capacity`]: ./struct.CacheString.html#method.capacity
343    ///
344    /// ```rust
345    /// # use arraystring::prelude::*;
346    /// # fn main() -> Result<(), OutOfBounds> {
347    /// # let _ = env_logger::try_init();
348    /// let string = CacheString::from_iterator(&["My String", " Other String"][..]);
349    /// assert_eq!(string.as_str(), "My String Other String");
350    ///
351    /// let out_of_bounds = (0..400).map(|_| "000");
352    /// let truncated = "0".repeat(CacheString::capacity().into());
353    ///
354    /// let truncate = CacheString::from_iterator(out_of_bounds);
355    /// assert_eq!(truncate.as_str(), truncated.as_str());
356    /// # Ok(())
357    /// # }
358    /// ```
359    #[inline]
360    pub fn from_iterator<U, I>(iter: I) -> Self
361    where
362        U: AsRef<str>,
363        I: IntoIterator<Item = U>,
364    {
365        CacheString(ArrayString::from_iterator(iter))
366    }
367
368    /// Creates new `CacheString` from string slice iterator assuming length is appropriate.
369    ///
370    /// # Safety
371    ///
372    /// It's UB if `iter.map(|c| c.len()).sum()` > [`capacity`].
373    ///
374    /// [`capacity`]: ./struct.CacheString.html#method.capacity
375    ///
376    /// ```rust
377    /// # use arraystring::prelude::*;
378    /// let string = unsafe {
379    ///     CacheString::from_iterator_unchecked(&["My String", " My Other String"][..])
380    /// };
381    /// assert_eq!(string.as_str(), "My String My Other String");
382    ///
383    /// // Undefined behavior, don't do it
384    /// // let out_of_bounds = (0..400).map(|_| "000");
385    /// // let undefined_behavior = unsafe {
386    /// //     CacheString::from_iterator_unchecked(out_of_bounds)
387    /// // };
388    /// ```
389    #[inline]
390    pub unsafe fn from_iterator_unchecked<U, I>(iter: I) -> Self
391    where
392        U: AsRef<str>,
393        I: IntoIterator<Item = U>,
394    {
395        CacheString(ArrayString::from_iterator_unchecked(iter))
396    }
397
398    /// Creates new `CacheString` from char iterator if total length is lower or equal to [`capacity`], otherwise returns an error.
399    ///
400    /// [`capacity`]: ./struct.CacheString.html#method.capacity
401    ///
402    /// ```rust
403    /// # use arraystring::{error::Error, prelude::*};
404    /// # fn main() -> Result<(), Error> {
405    /// # let _ = env_logger::try_init();
406    /// let string = CacheString::try_from_chars("My String".chars())?;
407    /// assert_eq!(string.as_str(), "My String");
408    ///
409    /// let out_of_bounds = "0".repeat(CacheString::capacity() as usize + 1);
410    /// assert!(CacheString::try_from_chars(out_of_bounds.chars()).is_err());
411    /// # Ok(())
412    /// # }
413    /// ```
414    #[inline]
415    pub fn try_from_chars<I>(iter: I) -> Result<Self, OutOfBounds>
416    where
417        I: IntoIterator<Item = char>,
418    {
419        Ok(CacheString(ArrayString::try_from_chars(iter)?))
420    }
421
422    /// Creates new `CacheString` from char iterator truncating size if bigger than [`capacity`].
423    ///
424    /// [`capacity`]: ./struct.CacheString.html#method.capacity
425    ///
426    /// ```rust
427    /// # use arraystring::prelude::*;
428    /// # let _ = env_logger::try_init();
429    /// let string = CacheString::from_chars("My String".chars());
430    /// assert_eq!(string.as_str(), "My String");
431    ///
432    /// let out_of_bounds = "0".repeat(CacheString::capacity() as usize + 1);
433    /// let truncated = "0".repeat(CacheString::capacity().into());
434    ///
435    /// let truncate = CacheString::from_chars(out_of_bounds.chars());
436    /// assert_eq!(truncate.as_str(), truncated.as_str());
437    /// ```
438    #[inline]
439    pub fn from_chars<I>(iter: I) -> Self
440    where
441        I: IntoIterator<Item = char>,
442    {
443        CacheString(ArrayString::from_chars(iter))
444    }
445
446    /// Creates new `CacheString` from char iterator assuming length is appropriate.
447    ///
448    /// # Safety
449    ///
450    /// It's UB if `iter.map(|c| c.len_utf8()).sum()` > [`capacity`].
451    ///
452    /// [`capacity`]: ./struct.CacheString.html#method.capacity
453    ///
454    /// ```rust
455    /// # use arraystring::prelude::*;
456    /// let string = unsafe { CacheString::from_chars_unchecked("My String".chars()) };
457    /// assert_eq!(string.as_str(), "My String");
458    ///
459    /// // Undefined behavior, don't do it
460    /// // let out_of_bounds = "000".repeat(400);
461    /// // let undefined_behavior = unsafe { CacheString::from_chars_unchecked(out_of_bounds.chars()) };
462    /// ```
463    #[inline]
464    pub unsafe fn from_chars_unchecked<I>(iter: I) -> Self
465    where
466        I: IntoIterator<Item = char>,
467    {
468        CacheString(ArrayString::from_chars_unchecked(iter))
469    }
470
471    /// Creates new `CacheString` from byte slice, returning [`Utf8`] on invalid utf-8 data or [`OutOfBounds`] if bigger than [`capacity`]
472    ///
473    /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
474    /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
475    /// [`capacity`]: ./struct.CacheString.html#method.capacity
476    ///
477    /// ```rust
478    /// # use arraystring::{error::Error, prelude::*};
479    /// # fn main() -> Result<(), Error> {
480    /// # let _ = env_logger::try_init();
481    /// let string = CacheString::try_from_utf8("My String")?;
482    /// assert_eq!(string.as_str(), "My String");
483    ///
484    /// let invalid_utf8 = [0, 159, 146, 150];
485    /// assert_eq!(CacheString::try_from_utf8(invalid_utf8), Err(Error::Utf8));
486    ///
487    /// let out_of_bounds = "0000".repeat(400);
488    /// assert_eq!(CacheString::try_from_utf8(out_of_bounds.as_bytes()), Err(Error::OutOfBounds));
489    /// # Ok(())
490    /// # }
491    /// ```
492    #[inline]
493    pub fn try_from_utf8<B>(slice: B) -> Result<Self, Error>
494    where
495        B: AsRef<[u8]>,
496    {
497        Ok(CacheString(ArrayString::try_from_utf8(slice)?))
498    }
499
500    /// Creates new `CacheString` from byte slice, returning [`Utf8`] on invalid utf-8 data, truncating if bigger than [`capacity`].
501    ///
502    /// [`Utf8`]: ./error/struct.Utf8.html
503    /// [`capacity`]: ./struct.CacheString.html#method.capacity
504    ///
505    /// ```rust
506    /// # use arraystring::{error::Error, prelude::*};
507    /// # fn main() -> Result<(), Error> {
508    /// # let _ = env_logger::try_init();
509    /// let string = CacheString::from_utf8("My String")?;
510    /// assert_eq!(string.as_str(), "My String");
511    ///
512    /// let invalid_utf8 = [0, 159, 146, 150];
513    /// assert_eq!(CacheString::from_utf8(invalid_utf8), Err(Utf8));
514    ///
515    /// let out_of_bounds = "0".repeat(300);
516    /// assert_eq!(CacheString::from_utf8(out_of_bounds.as_bytes())?.as_str(),
517    ///            "0".repeat(CacheString::capacity().into()).as_str());
518    /// # Ok(())
519    /// # }
520    /// ```
521    #[inline]
522    pub fn from_utf8<B>(slice: B) -> Result<Self, Utf8>
523    where
524        B: AsRef<[u8]>,
525    {
526        Ok(CacheString(ArrayString::from_utf8(slice)?))
527    }
528
529    /// Creates new `CacheString` from byte slice assuming it's utf-8 and of a appropriate size.
530    ///
531    /// # Safety
532    ///
533    /// It's UB if `slice` is not a valid utf-8 string or `slice.len()` > [`capacity`].
534    ///
535    /// [`capacity`]: ./struct.CacheString.html#method.capacity
536    ///
537    /// ```rust
538    /// # use arraystring::prelude::*;
539    /// let string = unsafe { CacheString::from_utf8_unchecked("My String") };
540    /// assert_eq!(string.as_str(), "My String");
541    ///
542    /// // Undefined behavior, don't do it
543    /// // let out_of_bounds = "0".repeat(300);
544    /// // let ub = unsafe { CacheString::from_utf8_unchecked(out_of_bounds)) };
545    /// ```
546    #[inline]
547    pub unsafe fn from_utf8_unchecked<B>(slice: B) -> Self
548    where
549        B: AsRef<[u8]>,
550    {
551        CacheString(ArrayString::from_utf8_unchecked(slice))
552    }
553
554    /// Creates new `CacheString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data or [`OutOfBounds`] if bigger than [`capacity`]
555    ///
556    /// [`Utf16`]: ./error/enum.Error.html#variant.Utf16
557    /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
558    /// [`capacity`]: ./struct.CacheString.html#method.capacity
559    ///
560    /// ```rust
561    /// # use arraystring::{error::Error, prelude::*};
562    /// # fn main() -> Result<(), Error> {
563    /// # let _ = env_logger::try_init();
564    /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
565    /// let string = CacheString::try_from_utf16(music)?;
566    /// assert_eq!(string.as_str(), "𝄞music");
567    ///
568    /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
569    /// assert_eq!(CacheString::try_from_utf16(invalid_utf16), Err(Error::Utf16));
570    ///
571    /// let out_of_bounds: Vec<_> = (0..300).map(|_| 0).collect();
572    /// assert_eq!(CacheString::try_from_utf16(out_of_bounds), Err(Error::OutOfBounds));
573    /// # Ok(())
574    /// # }
575    /// ```
576    #[inline]
577    pub fn try_from_utf16<B>(slice: B) -> Result<Self, Error>
578    where
579        B: AsRef<[u16]>,
580    {
581        Ok(CacheString(ArrayString::try_from_utf16(slice)?))
582    }
583
584    /// Creates new `CacheString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data, truncating if bigger than [`capacity`].
585    ///
586    /// [`Utf16`]: ./error/struct.Utf16.html
587    /// [`capacity`]: ./struct.CacheString.html#method.capacity
588    ///
589    /// ```rust
590    /// # use arraystring::{error::Error, prelude::*};
591    /// # fn main() -> Result<(), Error> {
592    /// # let _ = env_logger::try_init();
593    /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
594    /// let string = CacheString::from_utf16(music)?;
595    /// assert_eq!(string.as_str(), "𝄞music");
596    ///
597    /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
598    /// assert_eq!(CacheString::from_utf16(invalid_utf16), Err(Utf16));
599    ///
600    /// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
601    /// assert_eq!(CacheString::from_utf16(out_of_bounds)?.as_str(),
602    ///            "\0".repeat(CacheString::capacity().into()).as_str());
603    /// # Ok(())
604    /// # }
605    /// ```
606    #[inline]
607    pub fn from_utf16<B>(slice: B) -> Result<Self, Utf16>
608    where
609        B: AsRef<[u16]>,
610    {
611        Ok(CacheString(ArrayString::from_utf16(slice)?))
612    }
613
614    /// Creates new `CacheString` from `u16` slice, replacing invalid utf-16 data with `REPLACEMENT_CHARACTER` (\u{FFFD}) and truncating size if bigger than [`capacity`]
615    ///
616    /// [`capacity`]: ./struct.CacheString.html#method.capacity
617    ///
618    /// ```rust
619    /// # use arraystring::{error::Error, prelude::*};
620    /// # fn main() -> Result<(), Error> {
621    /// # let _ = env_logger::try_init();
622    /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
623    /// let string = CacheString::from_utf16_lossy(music);
624    /// assert_eq!(string.as_str(), "𝄞music");
625    ///
626    /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
627    /// assert_eq!(CacheString::from_utf16_lossy(invalid_utf16).as_str(), "𝄞mu\u{FFFD}ic");
628    ///
629    /// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
630    /// assert_eq!(CacheString::from_utf16_lossy(&out_of_bounds).as_str(),
631    ///            "\0".repeat(CacheString::capacity().into()).as_str());
632    /// # Ok(())
633    /// # }
634    /// ```
635    #[inline]
636    pub fn from_utf16_lossy<B>(slice: B) -> Self
637    where
638        B: AsRef<[u16]>,
639    {
640        CacheString(ArrayString::from_utf16_lossy(slice))
641    }
642
643    /// Returns maximum string capacity, defined at compile time, it will never change
644    ///
645    /// Should always return 63 bytes
646    ///
647    /// ```rust
648    /// # use arraystring::prelude::*;
649    /// # let _ = env_logger::try_init();
650    /// assert_eq!(CacheString::capacity(), 63);
651    /// ```
652    #[inline]
653    pub fn capacity() -> u8 {
654        <U63 as Unsigned>::to_u8()
655    }
656
657    /// Splits `CacheString` in two if `at` is smaller than `self.len()`.
658    ///
659    /// Returns [`Utf8`] if `at` does not lie at a valid utf-8 char boundary and [`OutOfBounds`] if it's out of bounds
660    ///
661    /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
662    /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
663    ///
664    /// ```rust
665    /// # use arraystring::{error::Error, prelude::*};
666    /// # fn main() -> Result<(), Error> {
667    /// # let _ = env_logger::try_init();
668    /// let mut s = CacheString::try_from_str("AB🤔CD")?;
669    /// assert_eq!(s.split_off(6)?.as_str(), "CD");
670    /// assert_eq!(s.as_str(), "AB🤔");
671    /// assert_eq!(s.split_off(20), Err(Error::OutOfBounds));
672    /// assert_eq!(s.split_off(4), Err(Error::Utf8));
673    /// # Ok(())
674    /// # }
675    /// ```
676    #[inline]
677    pub fn split_off(&mut self, at: u8) -> Result<Self, Error> {
678        Ok(CacheString(self.0.split_off(at)?))
679    }
680}
681
682impl Debug for CacheString {
683    #[inline]
684    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
685        f.debug_tuple("CacheString").field(&self.0).finish()
686    }
687}
688
689impl Hash for CacheString {
690    #[inline]
691    fn hash<H: Hasher>(&self, hasher: &mut H) {
692        self.0.hash(hasher);
693    }
694}
695
696impl PartialEq for CacheString {
697    #[inline]
698    fn eq(&self, other: &Self) -> bool {
699        self.0.eq(&other.0)
700    }
701}
702impl Eq for CacheString {}
703
704impl Ord for CacheString {
705    #[inline]
706    fn cmp(&self, other: &Self) -> Ordering {
707        self.0.cmp(&other.0)
708    }
709}
710
711impl PartialOrd for CacheString {
712    #[inline]
713    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
714        Some(self.cmp(other))
715    }
716}
717
718impl Deref for CacheString {
719    type Target = ArrayString<U63>;
720
721    #[inline]
722    fn deref(&self) -> &Self::Target {
723        &self.0
724    }
725}
726
727impl DerefMut for CacheString {
728    #[inline]
729    fn deref_mut(&mut self) -> &mut ArrayString<U63> {
730        &mut self.0
731    }
732}
733
734impl Display for CacheString {
735    #[inline]
736    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
737        Display::fmt(&self.0, f)
738    }
739}
740
741impl AsRef<str> for CacheString {
742    #[inline]
743    fn as_ref(&self) -> &str {
744        self.0.as_ref()
745    }
746}
747
748impl AsMut<str> for CacheString {
749    #[inline]
750    fn as_mut(&mut self) -> &mut str {
751        self.0.as_mut()
752    }
753}
754
755impl AsRef<[u8]> for CacheString {
756    #[inline]
757    fn as_ref(&self) -> &[u8] {
758        self.0.as_ref()
759    }
760}
761
762impl FromStr for CacheString {
763    type Err = OutOfBounds;
764
765    #[inline]
766    fn from_str(s: &str) -> Result<Self, Self::Err> {
767        Ok(CacheString(ArrayString::try_from_str(s)?))
768    }
769}
770
771impl<'a, 'b> PartialEq<str> for CacheString {
772    #[inline]
773    fn eq(&self, other: &str) -> bool {
774        self.0.eq(other)
775    }
776}
777
778impl Borrow<str> for CacheString {
779    #[inline]
780    fn borrow(&self) -> &str {
781        self.0.borrow()
782    }
783}
784impl BorrowMut<str> for CacheString {
785    #[inline]
786    fn borrow_mut(&mut self) -> &mut str {
787        self.0.borrow_mut()
788    }
789}
790
791impl<'a> Add<&'a str> for CacheString {
792    type Output = Self;
793
794    #[inline]
795    fn add(self, other: &str) -> Self::Output {
796        CacheString(self.0.add(other))
797    }
798}
799
800impl Write for CacheString {
801    #[inline]
802    fn write_str(&mut self, slice: &str) -> fmt::Result {
803        self.0.write_str(slice)
804    }
805}
806
807impl From<ArrayString<U63>> for CacheString {
808    fn from(array: ArrayString<U63>) -> Self {
809        CacheString(array)
810    }
811}