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}