fixedstr_ext/
lib.rs

1//! **Library for several alternative string types using const generics.**
2//!
3//!
4//!  - The size of some types such as [str8] and [zstr]\<8\>
5//!    are 8 bytes, compared to 16 bytes for `&str` on 64bit systems,
6//!    providing more efficient ways of representing small strings.
7//!  -  Most types (except the optional [Flexstr] and [Sharedstr]) can be
8//!    copied and stack-allocated.
9//!  -  `#![no_std]` is supported by all but the optional [fstr] type.
10//!     Features that use the alloc crate can also be optionally excluded.
11//!  -  Unicode is supported by all but the optional [cstr] type.
12//!  -  Serde serialization is supported by all but the optional [Sharedstr] type.
13//!  -  Select functions are `const`, including const constructors.
14//!
15//!
16//! **COMPATIBILITY NOTICES**:
17//!
18//! > **With Version 0.5.0, the default availability of some
19//!   string types have changed.**  The default configuration is minimalized.
20//!   The `std`, `flex-str` and `shared-str`
21//!   options are no longer enabled by default.  The crate now
22//!   supports **`#![no_std]`** by default.  The `std` option only enables the
23//!   [fstr] type, which prints warnings to stderr. **However,** unless
24//!   you require one of the types [fstr], [Flexstr] or [Sharedstr], your
25//!   build configurations most likely will work as before: the builds will just be
26//!   smaller.  If `default-features=false` is already part of your
27//!   configuration, it should also work as before.
28//!
29//! > Another change that could potentially affect backwards compatibility is that
30//!   zstr's `Index<usize>` and `IndexMut<usize>` traits, which allow
31//!   arbitrary modifications to underlying bytes, is now only available
32//!   with the optional `experimental` feature.  Previously, they were
33//!   available as default features.
34//!
35//! **Other Important Recent Updates:**
36//!
37//! >  **Version 0.5.1 introduced the new *`no-alloc`* option**.  In addition to support
38//!    for no_std (for all but the fstr type), this option disables compilation of
39//!    any features that use the alloc crate.  This may make some no_std implementations
40//!    easier. The default build is no longer minimal (see below).
41//!
42//! >  As of Version 0.4.6, all string types except for `fstr` support
43//! **`#![no_std]`**.
44//!
45//! >  Starting in Version 0.4.2, the underlying representation of the zero-terminated [zstr]
46//! type no longer allows non-zero bytes after the first zero.  In particular,
47//! the [zstr::from_raw] function now enforces this rule.
48//!
49//! >  Starting in Version 0.4.0, warnings about
50//! capacity being exceeded are only sent to stderr when using the fstr type.
51//! For other types, truncation is done silently. Consider using the
52//! `try_make` function or the [core::str::FromStr] trait.
53//!
54//! <hr>
55//!
56//! **CRATE OVERVIEW**
57//!
58//! The two string types that are always provided by this crate are **[zstr]** and **[tstr]**.
59//! However, [tstr] is not public by default and should be referenced
60//! through the type aliases [str4], [str8], [str16], ...  [str256].
61//!
62//! - A **[zstr]\<N\>** is represented by a `[u8;N]` array underneath
63//!   and can hold zero-terminated, utf-8 strings of up to N-1 bytes.
64//! Furthermore, no non-zero bytes can follow the first zero. This
65//! allows the length of a `zstr<N>` string to be found in O(log N) time.
66//!
67//! - The types **[str4]** through **[str256]** are aliases for internal types
68//! [tstr]\<4\> through [tstr]\<256\> respectively.  These strings are stored
69//! in `[u8;N]` arrays with the first byte holding the length of the
70//! string.  Each `tstr<N>` can store strings of up to N-1 bytes, with
71//! maximum N=256. Because Rust does not currently provide
72//! a way to specify conditions (or type casts) on const generics at
73//! compile time, the tstr type is not public by
74//! default and can only be used through the aliases.  The `pub-tstr` option
75//! makes the `tstr` type public but is not recommended: any `tstr<N>` with
76//! `N>256` is not valid and will result in erroneous behavior.
77//!
78//! In addition, the following string types are available as options:
79//!
80//! - A **[fstr]\<N\>** stores a string of up to N bytes.
81//! It's represented by a `[u8;N]` array and a separate usize variable
82//! holding the length.  This type is **enabled with either the `std` or
83//! `fstr` option** and some functions will print warnings to stderr when
84//! capacity is exceeded. This is the only type that does not support
85//! `no_std`, but serde is supported.
86//! - The type **[cstr]**, which is **made available
87//! with the `circular-str` option**, uses a fixed u8 array
88//! that is arranged as a circular queue (aka ring buffer).  This allows
89//! efficient implementations of pushing/triming characters *in front* of
90//! the string without additional memory allocation.  The downside of these
91//! strings is that the underlying representation can be non-contiguous as it allows
92//! wrap-around.  As a result, there is no efficient way to implement
93//! `Deref<str>`.  Additionally, cstr is the only string type of the crate
94//! that does not support Unicode. **Only single-byte characters** are
95//! currently supported. There is, however, an iterator over all characters
96//! and most common traits are implemented.  Serde and no-std are both supported.
97//! - The **[Flexstr]\<N\>** type becomes available with the **`flex-str` option**.
98//!   This type uses an internal enum that is either a tstr\<N\>
99//!   or an owned String (alloc::string::String) in case the length of the string exceeds N-1.
100//!   This type is designed for situations where strings only
101//!   occasionally exceed the limit of N-1 bytes. This type does not implement
102//!   the `Copy` trait.  Serde and no_std are supported.
103//! - The **[Sharedstr]\<N\>** type becomes available with the **`shared-str`
104//!   option**. This type is similar to a [Flexstr]\<N\> but uses a
105//!   `Rc<RefCell<..>>` underneath to allow strings to be shared as well as
106//!   mutated.  This type does not implement `Copy` but `Clone` is done
107//!   in constant time.  no_std is supported but **not serde**.
108//!
109//! **SUMMARY OF OPTIONAL FEATURES**
110//!
111//! - ***serde*** : Serialization was initially contributed
112//!   by [wallefan](https://github.com/wallefan) and adopted to other types
113//!   (except `Sharedstr`).  This feature enables the Serialize/Deserialize
114//!   traits.
115//! - ***circular-str***: this feature makes available the **[cstr]** type.
116//! - ***flex-str***: this feature makes available the **[Flexstr]** type.  
117//! - ***shared-str***: this feature makes available the **[Sharedstr]** type.
118//! - ***std***: this feature cancels `no_std` by enabling the **[fstr]** type.
119//!   An alias for this feature name is 'fstr'.
120//! - ***pub-tstr***: this feature will make the tstr type public. It is not
121//!   recommended: use instead the type aliases [str4] - [str256], which are
122//!   always available.
123//! - **no-alloc**: this *anti-feature* disables any features that requires the alloc (or std)
124//!   crate.  It will disable *entirely* the fstr, Flexstr and Sharedstr types: using
125//!   `no-alloc` together with `flex-str`, for example, will not enable the Flexstr type.
126//!   It also disables the features in [tstr], [zstr] and [cstr] that require the
127//!   alloc crate, in particular any use of alloc::string::String.  Using this feature
128//!   is *stronger than no_std*.  Note that when compiled with the `all-features` option, this feature will be included, which will exclude other features.
129//! - ***experimental***: the meaning of this feature may change.  Currently
130//!   it implements custom Indexing traits for the zstr type, including
131//!   `IndexMut<usize>`, which allows individual bytes to be changed
132//!   arbitrarily.  Experimental features are not part of the documentation.
133//!
134//! None of these features is provided by default, so specifying
135//! `default-features=false` has no effect.
136//!
137//! **SAMPLE BUILD CONFIGURATIONS**
138//!
139//! The simplest way to install this create is to **`cargo add fixedstr`** in your
140//! crate or add `fixedstr = "0.5"` to your dependencies in Cargo.toml.
141//! The default build makes available the [zstr] type and the type aliases
142//! [str4] - [str256] for [tstr].  Serde is not available with this build
143//! but no_std is supported, substituting some std features with those from the
144//! alloc crate.
145//!
146//! For **the smallest possible build**, do **`cargo add fixedstr --features no-alloc`**
147//! in your crate or add the following in Cargo.toml.
148//! ```ignore
149//!   [dependencies]
150//!   fixedstr = {version="0.5", features=["no-alloc"]}
151//! ```
152//!
153//! To further enable serde serialization, add the following instead:
154//! ```ignore
155//!   [dependencies]
156//!   fixedstr = {version="0.5", features=["serde","no-alloc"]}
157//! ```
158//! and to exclude `cstr` but include all other features (except `no-alloc`):
159//! ```ignore
160//!   [dependencies]
161//!   fixedstr = {version="0.5", features=["std","flex-str","shared-str","serde","pub-tstr","experimental"]}
162//! ```
163//! <br>
164//!
165//! **Do not** install this crate with the `--all-features` option unless you
166//! understand that it would include `no-alloc`, which will disable several
167//! types and other features of the crate.
168//!
169//!  ## Examples
170//!
171//!```
172//! use fixedstr_ext::*;
173//! let a = str8::from("abcdefg"); //creates new string from &str
174//! let a1 = a; // copied, not moved
175//! let a2:&str = a.to_str();
176//! let a3:String = a.to_string();
177//! assert_eq!(a.nth_ascii(2), 'c');
178//! let ab = a.substr(1,5);  // copies substring to new str8
179//! assert_eq!(ab,"bcde");  // can compare with &str
180//! assert_eq!(&a[1..4],"bcd"); // implements Index
181//! assert!(a<ab);  // implements Ord (and Hash, Debug, Display, other traits)
182//! let mut u:zstr<8> = zstr::from("aλb"); //unicode support
183//! {assert_eq!(u.nth(1).unwrap(),'λ');} // nth returns Option<char>
184//! assert!(u.set(1,'μ'));  // changes a character of the same character class
185//! assert!(!u.set(1,'c')); // .set returns false on failure
186//! assert!(u.set(2,'c'));
187//! assert_eq!(u, "aμc");
188//! assert_eq!(u.len(),4);  // length in bytes
189//! assert_eq!(u.charlen(),3);  // length in chars
190//! let mut ac:str16 = a.resize(); // copies to larger capacity string
191//! let remainder:&str = ac.push("hijklmnopqrst");  //appends string, returns left over
192//! assert_eq!(ac.len(),15);
193//! assert_eq!(remainder, "pqrst");
194//! ac.truncate(10); // shortens string in place
195//! assert_eq!(&ac,"abcdefghij");
196//! let (upper,lower) = (str8::make("ABC"), str8::make("abc"));
197//! assert_eq!(upper, lower.to_ascii_upper()); // no owned String needed
198//!  
199//! let c1 = str8::from("abcdef"); // string concatenation with + for strN types  
200//! let c2 = str8::from("xyz123");
201//! let c3 = c1 + c2;       
202//! assert_eq!(c3,"abcdefxyz123");   
203//! assert_eq!(c3.capacity(),15);  // type of c3 is str16
204//!
205//! let c4 = str_format!(str16,"abc {}{}{}",1,2,3); // impls core::fmt::Write
206//! assert_eq!(c4,"abc 123");  // str_format! truncates if capacity exceeded
207//! let c5 = try_format!(str8,"abcdef{}","ghijklmn");
208//! assert!(c5.is_none());  // try_format! returns None if capacity exceeded
209//!
210//! #[cfg(feature = "shared-str")]
211//! #[cfg(not(feature = "no-alloc"))]
212//! {
213//!   let mut s:Sharedstr<8> = Sharedstr::from("abcd");
214//!   let mut s2 = s.clone(); // O(1) cost
215//!   s.push_char('e');
216//!   s2.set(0,'A');
217//!   assert_eq!(s2, "Abcde");
218//!   assert!(s==s2 && s.ptr_eq(&s2));
219//! }
220//!
221//! #[cfg(feature = "experimental")]
222//! {
223//!   let mut s = <zstr<8>>::from("abcd");
224//!   s[0] = b'A';       // implements IndexMut<usize> (only for zstr)
225//!   assert_eq!(&s[0..3],"Abc");
226//! }
227//! ```
228//!
229// #![doc = document_features::document_features!()]
230
231#![allow(unused_variables)]
232#![allow(non_snake_case)]
233#![allow(non_camel_case_types)]
234#![allow(unused_parens)]
235#![allow(unused_assignments)]
236#![allow(unused_mut)]
237#![allow(unused_imports)]
238#![allow(dead_code)]
239#![no_std]
240
241#[cfg(feature = "std")]
242#[cfg(not(feature = "no-alloc"))]
243mod full_fixed;
244#[cfg(feature = "std")]
245#[cfg(not(feature = "no-alloc"))]
246pub use full_fixed::*;
247
248//#[cfg(feature = "flex-str")]
249//mod shared_structs;
250
251#[cfg(not(feature = "no-alloc"))]
252#[cfg(any(feature = "shared-str", feature = "flex-str"))]
253mod shared_structs;
254
255#[cfg(feature = "flex-str")]
256#[cfg(not(feature = "no-alloc"))]
257mod flexible_string;
258#[cfg(feature = "flex-str")]
259#[cfg(not(feature = "no-alloc"))]
260pub use flexible_string::*;
261
262#[cfg(feature = "shared-str")]
263#[cfg(not(feature = "no-alloc"))]
264mod shared_string;
265#[cfg(feature = "shared-str")]
266#[cfg(not(feature = "no-alloc"))]
267pub use shared_string::*;
268
269mod zero_terminated;
270pub use zero_terminated::*;
271
272mod tiny_internal;
273use tiny_internal::*;
274#[cfg(feature = "pub_tstr")]
275pub use tiny_internal::*;
276
277#[cfg(feature = "circular-str")]
278mod circular_string;
279#[cfg(feature = "circular-str")]
280pub use circular_string::*;
281
282#[cfg(not(feature = "no-alloc"))]
283extern crate alloc;
284
285/*
286#[cfg(feature = "compressed-str")]
287#[cfg(not(feature = "no-alloc"))]
288mod compressed;
289#[cfg(feature = "compressed-str")]
290#[cfg(not(feature = "no-alloc"))]
291pub use compressed::*;
292*/
293
294//////// Unifying Trait Approach
295#[cfg(feature = "experimental")]
296pub trait Fixedstr<const N: usize> {
297    type Target;
298
299    fn make<TA: AsRef<str>>(s: TA) -> Self::Target;
300
301    fn create<TA: AsRef<str>>(s: TA) -> Self::Target {
302        Self::make(s)
303    } // won't compile
304    fn try_make<TA: AsRef<str>>(s: TA) -> Result<Self::Target, TA> {
305        if s.as_ref().len() < N {
306            Ok(Self::make(s))
307        } else {
308            Err(s)
309        }
310    }
311
312    /*const*/
313    fn const_make(s: &str) -> Self::Target;
314    /*const*/
315    fn const_try_make(s: &str) -> Option<Self::Target>;
316
317    fn new() -> Self::Target {
318        Self::make("")
319    }
320
321    /*const*/
322    fn len(&self) -> usize;
323    fn charlen(&self) -> usize;
324    /*const*/
325    fn capacity(&self) -> usize {
326        N - 1
327    }
328
329    fn to_str(&self) -> &str;
330    fn as_str(&self) -> &str;
331
332    #[cfg(not(feature = "no-alloc"))]
333    fn to_string(&self) -> alloc::string::String {
334        alloc::string::String::from(self.to_str())
335    }
336
337    fn set(&mut self, i: usize, c: char) -> bool;
338
339    fn push_str<'t>(&mut self, src: &'t str) -> &'t str;
340
341    fn push_char(&mut self, c: char) -> bool;
342
343    fn pop_char(&mut self) -> Option<char>;
344
345    fn nth(&self, n: usize) -> Option<char>;
346
347    fn nth_bytechar(&self, n: usize) -> char;
348
349    fn truncate(&mut self, n: usize);
350
351    fn truncate_bytes(&mut self, n: usize);
352
353    fn truncate_unchecked(&mut self, n: usize);
354
355    fn clear(&mut self);
356
357    fn right_ascii_trim(&mut self);
358
359    fn make_ascii_lowercase(&mut self);
360
361    fn make_ascii_uppercase(&mut self);
362
363    fn case_insensitive_eq<TA: AsRef<str>>(&self, other: TA) -> bool;
364
365    fn is_ascii(&self) -> bool {
366        self.to_str().is_ascii()
367    }
368} //trait Fixedstr
369
370//#[macro_use]
371//extern crate static_assertions;
372
373#[cfg(feature = "serde")]
374mod serde_support {
375    use super::*;
376    use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
377    macro_rules! generate_impl {
378        ($ty: ident, $visitor: ident) => {
379            impl<const N: usize> Serialize for $ty<N> {
380                fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
381                    serializer.serialize_str(self.as_str())
382                }
383            }
384            impl<'de, const N: usize> Deserialize<'de> for $ty<N> {
385                fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
386                    deserializer.deserialize_str($visitor)
387                }
388            }
389            struct $visitor<const N: usize>;
390            impl<'de, const N: usize> Visitor<'de> for $visitor<N> {
391                type Value = $ty<N>;
392                fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
393                    f.write_str("a string")
394                }
395                fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
396                    $ty::try_make(s).map_err(|_| E::custom("string too long"))
397                }
398            }
399        };
400    }
401    generate_impl!(zstr, ZstrVisitor);
402    generate_impl!(tstr, TstrVisitor);
403    #[cfg(feature = "std")]
404    #[cfg(not(feature = "no-alloc"))]
405    generate_impl!(fstr, FstrVisitor);
406    #[cfg(feature = "flex-str")]
407    #[cfg(not(feature = "no-alloc"))]
408    generate_impl!(Flexstr, FlexstrVisitor);
409
410    #[cfg(feature = "circular-str")]
411    impl<const N: usize> Serialize for cstr<N> {
412        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
413            let s = self.to_contiguous(); //self.to_string();
414            let (a, _) = s.to_strs();
415            serializer.serialize_str(a)
416        }
417    } //serialize
418
419    #[cfg(feature = "circular-str")]
420    struct CstrVisitor<const N: usize>;
421    #[cfg(feature = "circular-str")]
422    impl<'de, const N: usize> Visitor<'de> for CstrVisitor<N> {
423        type Value = cstr<N>;
424        fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
425            f.write_str("a string")
426        }
427        fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
428            cstr::try_make(s).map_err(|_| E::custom("string too long"))
429        }
430    }
431
432    #[cfg(feature = "circular-str")]
433    impl<'de, const N: usize> Deserialize<'de> for cstr<N> {
434        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
435            deserializer.deserialize_str(CstrVisitor)
436        }
437    }
438} //serde
439
440/// Types for small strings that use an efficient representation
441/// underneath.  Alias for internal type [tstr]\<8\>.
442/// A str8 is 8 bytes and can hold string of up to 7 bytes.
443/// See documentation for the aliased [tstr] type.
444///
445/// Example:
446/// ```
447///  # use fixedstr_ext::str8;
448///  let mut s = str8::from("aλc");
449///  assert_eq!(s.capacity(),7);
450///  assert_eq!(s.push("1234567"), "4567");
451///  assert_eq!(s,"aλc123");
452///  assert_eq!(s.charlen(), 6);
453///  assert_eq!(s.len(), 7);  
454/// ```
455
456pub type str8 = tstr<8>;
457/// A str16 can hold a string of up to 15 bytes. See docs for [fstr] or [zstr].
458/// The size of str16 is 16 bytes, which is the same as for &str on 64bit
459/// systems.
460pub type str16 = tstr<16>;
461/// A str32 can hold a string of up to 31 bytes. See docs for [fstr] or [zstr]
462pub type str32 = tstr<32>;
463/// A str64 can hold a string of up to 63 bytes. See docs for [fstr] or [zstr]
464pub type str64 = tstr<64>;
465/// A str28 can hold a string of up to 127 bytes. See docs for [fstr] or [zstr]
466pub type str128 = tstr<128>;
467
468/// Each type strN is represented underneath by a `[u8;N]` with N<=256.
469/// The first byte of the array always holds the length of the string.
470/// Each such type can hold a string of up to N-1 bytes, with max size=255.
471/// These types represent the best combination of [fstr] and [zstr] in
472/// terms of speed and memory efficiency.
473///<br>
474/// In addition, the str4-str128 types implement [core::ops::Add] in a way that
475/// two str8 strings will always concatenate to str16, and similarly for
476/// all other strN types up to str128.
477///```
478///  # use fixedstr_ext::*;
479///  let c1 = str8::from("abcd");
480///  let c2 = str8::from("xyz");
481///  let c3 = c1 + c2;
482///  assert_eq!(c3,"abcdxyz");
483///  assert_eq!(c3.capacity(),15);
484///```
485
486pub type str256 = tstr<256>;
487
488/// Alias for internal type `tstr<4>`.
489/// <br>Holds strings of up to three single-byte chars, good enough to represent abbreviations
490/// such as those for states and airports. Each str<4> is exactly 32 bits.
491/// Alias for internal type `tstr<4>`.   See documentation for [tstr].
492pub type str4 = tstr<4>;
493pub type str12 = tstr<12>;
494pub type str24 = tstr<24>;
495pub type str48 = tstr<48>;
496pub type str96 = tstr<96>;
497pub type str192 = tstr<192>;
498
499#[macro_export]
500/// creates a formated string of given type (by implementing [core::fmt::Write]):
501/// ```
502///    # use fixedstr_ext::*;
503///    let s = str_format!(str8,"abc{}{}{}",1,2,3);
504///    assert_eq!(s,"abc123");
505/// ```
506/// will truncate if capacity exceeded, without warning. See [try_format!]
507/// for version that does not truncate.
508macro_rules! str_format {
509  ($ty_size:ty, $($args:tt)*) => {
510     {use core::fmt::Write;
511     let mut fstr0 = <$ty_size>::new();
512     let res=write!(&mut fstr0, $($args)*);
513     fstr0}
514  };
515}
516
517#[macro_export]
518/// version of [str_format]! that returns an Option of the given type.
519/// ```
520///   # use fixedstr_ext::*;
521///  let s = try_format!(str32,"abcdefg{}","hijklmnop").unwrap();
522///  let s2 = try_format!(str8,"abcdefg{}","hijklmnop");
523///  assert!(s2.is_none());
524/// ```
525macro_rules! try_format {
526  ($ty_size:ty, $($args:tt)*) => {
527     {use core::fmt::Write;
528     let mut fstr0 = <$ty_size>::new();
529     let result = write!(&mut fstr0, $($args)*);
530     if result.is_ok() {Some(fstr0)} else {None}}
531  };
532}
533
534/*
535//////////// to string trait
536pub trait ToTstr<const N: usize> {
537  fn to_tstr(&self) -> tstr<N>;
538}//tostring trait
539*/
540
541#[macro_export]
542/// Macro for converting any expression that implements the Display trait
543/// into the specified type, similar to `to_string` but without necessary
544/// heap allocation.  Truncation is automatic and silent. Example:
545///```
546///  # use fixedstr_ext::*;
547///  let fs = to_fixedstr!(str8,-0132*2);
548///  assert_eq!(&fs,"-264");
549///```
550/// For version that does not truncate, use [convert_to_str!].
551macro_rules! to_fixedstr {
552    ($ty_size:ty, $x:expr) => {{
553        use core::fmt::Write;
554        let mut fstr0 = <$ty_size>::new();
555        let res = write!(&mut fstr0, "{}", $x);
556        fstr0
557    }};
558}
559
560#[macro_export]
561/// Version of [to_fixedstr!] that returns None instead of truncating .
562///```
563///  # use fixedstr_ext::*;
564///  let fsopt = convert_to_str!(zstr<16>,0.013128009);
565///  assert!(matches!(fsopt.as_deref(),Some("0.013128009")))
566///```
567macro_rules! convert_to_str {
568    ($ty_size:ty, $x:expr) => {{
569        use core::fmt::Write;
570        let mut fstr0 = <$ty_size>::new();
571        let res = write!(&mut fstr0, "{}", $x);
572        if res.is_ok() {
573            Some(fstr0)
574        } else {
575            None
576        }
577    }};
578}
579
580/////////////////////////////////////////////////////  Testing ...
581
582#[cfg(test)]
583mod tests {
584    use super::*;
585    #[test]
586    fn testmain() {
587        nostdtest();
588        ztests();
589
590        #[cfg(feature = "std")]
591        #[cfg(not(feature = "no-alloc"))]
592        maintest();
593        #[cfg(all(feature = "flex-str", feature = "std"))]
594        #[cfg(not(feature = "no-alloc"))]
595        flextest();
596        #[cfg(feature = "std")]
597        #[cfg(not(feature = "no-alloc"))]
598        tinytests();
599        #[cfg(all(feature = "std", feature = "flex-str"))]
600        #[cfg(not(feature = "no-alloc"))]
601        poppingtest();
602        #[cfg(all(feature = "std", feature = "shared-str"))]
603        #[cfg(not(feature = "no-alloc"))]
604        strptrtests();
605        #[cfg(feature = "pub-tstr")]
606        consttests();
607    } //testmain
608
609    #[cfg(feature = "std")]
610    #[cfg(feature = "shared-str")]
611    #[cfg(not(feature = "no-alloc"))]
612    fn strptrtests() {
613        extern crate std;
614        use std::fmt::Write;
615        use std::string::String;
616        let mut a = Sharedstr::<8>::from("abc12");
617        let mut b = a.clone();
618        let mut c = Sharedstr::<8>::from("abc");
619        c.push_str("12");
620        assert!(a == c);
621        assert!(a == "abc12");
622        b.push('3');
623        assert!(a == "abc123");
624        assert!("abc123" == b);
625    } //strptrtests
626
627    /// test struct
628    struct AB(i32, u32);
629    impl core::fmt::Display for AB {
630        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
631            write!(f, "{},{}", self.0, self.1)
632        }
633    }
634
635    #[cfg(all(feature = "std", feature = "flex-str"))]
636    #[cfg(not(feature = "no-alloc"))]
637    fn poppingtest() {
638        extern crate std;
639        use std::println;
640        let mut a = Flexstr::<8>::from("abcdef");
641        assert_eq!(a.pop_char().unwrap(), 'f');
642        println!("a: {}", &a);
643        let a = flexstr16::from("abcd");
644        let c: flexstr16 = &a + "efg";
645        assert_eq!(&c, "abcdefg");
646
647        let mut ab = AB(-5, 22 + 1);
648        let abfs = to_fixedstr!(zstr<16>, &ab);
649        assert_eq!(&abfs, "-5,23");
650        let abfs2 = convert_to_str!(zstr<3>, 10003);
651        assert!(abfs2.is_none());
652    } //poppingtest
653
654    fn nostdtest() {
655        let a: str8 = str8::from("abcdef"); //a str8 can hold up to 7 bytes
656        let a2 = a; // copied, not moved
657        let ab = a.substr(1, 5); // copies substring to new string
658        assert_eq!(ab, "bcde"); // compare for equality with &str
659        assert_eq!(&a[..3], "abc"); // impls Deref<str>
660        assert!(a < ab); // and Ord, Hash, Eq, Debug, Display, other common traits
661        let astr: &str = a.to_str(); // convert to &str
662        let azstr: zstr<16> = zstr::from(a); // so is zstr
663        let mut a32: str32 = a.resize(); // same kind of string but with 31-byte capacity
664        a32 = "abc" + a32;
665        let mut u = str8::from("aλb"); //unicode support
666        assert_eq!(u.nth(1), Some('λ')); // get nth character
667        assert_eq!(u.nth_bytechar(3), 'b'); // get nth byte as ascii character
668        assert!(u.set(1, 'μ')); // changes a character of the same character class
669        assert!(!u.set(1, 'c')); // .set returns false on failure
670        assert!(u.set(2, 'c'));
671        assert_eq!(u, "aμc");
672        assert_eq!(u.len(), 4); // length in bytes
673        assert_eq!(u.charlen(), 3); // length in chars
674        let mut ac: str16 = a.reallocate().unwrap(); //copies to larger capacity type
675        let remainder = ac.push_str("ghijklmnopq"); //append up to capacity, returns remainder
676        assert_eq!(ac.len(), 15);
677        assert_eq!(remainder, "pq");
678        ac.truncate(9); // keep first 9 chars
679        assert_eq!(&ac, "abcdefghi");
680        let (upper, lower) = (str8::make("ABC"), str8::make("abc"));
681        assert_eq!(upper, lower.to_ascii_upper()); // no owned String needed
682
683        let c1 = str8::from("abcd"); // string concatenation with + for strN types
684        let c2 = str8::from("xyz");
685        assert!(c2.case_insensitive_eq("XyZ"));
686        let c2b = str16::from("xYz");
687        assert!(c2.case_insensitive_eq(&c2b));
688        let mut c3 = c1 + c2;
689        assert_eq!(c3, "abcdxyz");
690        assert_eq!(c3.capacity(), 15); // type of c3 is str16
691        c3 = "00" + c3 + "."; // cat with &str on left or right
692        assert_eq!(c3, "00abcdxyz.");
693
694        let c4 = str_format!(str16, "abc {}{}{}", 1, 2, 3); // impls std::fmt::Write
695        assert_eq!(c4, "abc 123"); //str_format! truncates if capacity exceeded
696        let c5 = try_format!(str8, "abcdef{}", "ghijklmn");
697        assert!(c5.is_none()); // try_format! returns None if capacity exceeded
698
699        let fs = to_fixedstr!(str8, -0132);
700        assert_eq!(&fs, "-132");
701
702        // testing for constants
703        const C: str16 = str16::const_make("abcd");
704        //const C:zstr<8> = zstr::const_make("abcd");
705        let xarray = [0u8; C.len()];
706        assert_eq!(C, "abcd");
707        assert_eq!(xarray.len(), 4);
708
709        //cstr tests
710        #[cfg(feature = "circular-str")]
711        {
712            use crate::circular_string::*;
713            let mut cb = cstr::<16>::make("abc123");
714            assert!(cb.is_contiguous());
715            cb.push_str("xyz");
716            cb.push_front("9876");
717            assert_eq!(cb.pop_char().unwrap(), 'z');
718            assert_eq!(cb.pop_char_front().unwrap(), '9');
719            cb.push_str_front("000");
720            assert_eq!(cb.len(), 14);
721            assert!(&cb == "000876abc123xy");
722            cb.truncate_left(10);
723            assert_eq!(&cb, "23xy");
724            cb.push_str("ijklmno  ");
725            cb.push_char_front(' ');
726            assert!(&cb == " 23xyijklmno  ");
727            assert!(!cb.is_contiguous());
728            //  cb.trim_left();
729            //  assert!(&cb == "23xyijklmno ");
730            //  cb.trim_right();
731            cb.trim_whitespaces();
732            assert!("23xyijklmno" == &cb);
733            assert!(&cb < "4abc");
734
735            let mut a = cstr::<8>::make("12345678");
736            assert_eq!(a.len(), 8);
737            a.truncate_front(4);
738            assert_eq!(a.len(), 4);
739            assert!(a.is_contiguous());
740            assert!(&a == "5678");
741            a.push_str("abc");
742            assert!(&a == "5678abc");
743            let mut findopt = a.find_substr("8abc");
744            assert_eq!(findopt.unwrap(), 3);
745            findopt = a.rfind_substr("678abc");
746            assert_eq!(findopt.unwrap(), 1);
747            let mut rem = a.push_str("123456");
748            assert_eq!(rem, "23456");
749            a.truncate_left(4);
750            assert_eq!(&a, "abc1");
751            rem = a.push_front("qrstuvw");
752            assert_eq!(&a, "tuvwabc1");
753            assert_eq!(rem, "qrs");
754            rem = a.push_str("");
755            assert_eq!(&a, "tuvwabc1");
756            assert_eq!(rem, "");
757            a.truncate(5);
758            let mut ba = "123" + a;
759            assert_eq!(ba, "123tuvwa");
760            ba.truncate_left(4);
761            a.truncate_left(1);
762            assert_eq!(a, ba);
763
764            #[cfg(feature = "std")]
765            {
766                let bb = cstr::<8>::from("qgg");
767                extern crate std;
768                use std::collections::HashSet;
769                let mut hh = HashSet::new();
770                hh.insert(bb);
771                assert!(hh.get(&bb).is_some());
772            }
773        } //cstr tests
774    } //nostdtest
775
776    fn ztests() {
777        let a: zstr<8> = zstr::from("abcdefg"); //creates zstr from &str
778        let ab = a.substr(1, 5); // copies, not move substring to new string
779        assert_eq!(ab, "bcde"); // can compare equality with &str
780        assert!(ab.case_insensitive_eq("bCdE"));
781        let mut u: zstr<8> = zstr::from("aλb"); //unicode support
782        assert!(u.set(1, 'μ')); // changes a character of the same character class
783        assert!(!u.set(1, 'c')); // .set returns false on failure
784        assert!(u.set(2, 'c'));
785        assert_eq!(u, "aμc");
786        assert_eq!(u.len(), 4); // length in bytes
787        assert_eq!(u.charlen(), 3); // length in chars
788        let mut ac: zstr<16> = a.resize(); // copies to larger capacity string
789        let remainder = ac.push("hijklmnopqrst"); //appends string, returns left over
790        assert_eq!(ac.len(), 15);
791        assert_eq!(remainder, "pqrst");
792        ac.truncate(10);
793        assert_eq!(&ac, "abcdefghij");
794        //println!("ac {}, remainder: {}, len {}", &ac, &remainder, &ac.len());
795        assert_eq!(ac.len(), 10);
796        ac.pop_char();
797        ac.pop_char();
798        assert_eq!(ac.len(), 8);
799        let mut c4 = str_format!(zstr<16>, "abc {}", 123);
800        assert_eq!(c4, "abc 123");
801        let rem = c4.push_str("123456789abcdef");
802        assert_eq!(c4, "abc 12312345678");
803        assert_eq!(rem, "9abcdef");
804
805        let b = [65u8, 66, 67, 0, 0, 68, 0, 69, 0, 70, 0, 71];
806        let mut bz: zstr<16> = zstr::from_raw(&b);
807        bz.push("abcd   \t \n\n");
808        //println!("bz: {}, len {}", &bz, bz.len());
809        bz.right_ascii_trim();
810        bz.reverse_bytes();
811        bz.make_ascii_lowercase();
812        //println!("bz after trim, reverse: {}, len {}", &bz, bz.len());
813    } //ztr tests
814
815    #[cfg(feature = "std")]
816    #[cfg(not(feature = "no-alloc"))]
817    fn maintest() {
818        extern crate std;
819        use std::fmt::Write;
820        use std::println;
821        use std::string::String;
822        let s1: fstr<16> = fstr::from("abc");
823        let mut s2: fstr<8> = fstr::from("and xyz");
824        let s2r = s2.push(" and 1234");
825        println!("s1,s2,s2r,s2.len: {}, {}, {}, {}", s1, &s2, &s2r, s2.len());
826        println!("{}", &s1 == "abc");
827        let s3 = s1; // copied, not moved
828        println!("{}", "abc" == &s1);
829        println!("{}, {} ", s1 == s3, s1 == s2.resize());
830
831        let mut s4: fstr<256> = s3.resize();
832        s4.push("ccccccccccccccccccccccccccccccccccccccccccccccccccccccz");
833        println!("{}, length {}", &s4, s4.len());
834        let mut s5: fstr<32> = s4.resize();
835        println!("{}, length {}", &s5, s5.len());
836        println!("{:?}, length {}", &s5[0..10], s5.len());
837        println!("s2.substr {}", s2.substr(2, 6));
838        println!("{}", s2.substr(2, 6).len());
839        let mut s4: fstr<64> = s1.resize();
840        let owned_string: String = s4.to_string();
841        println!("owned s4: {}", &owned_string);
842        let str_slice: &str = s4.to_str();
843        println!("as &str: {}", &str_slice[0..2]);
844        s4 = s1.resize();
845        let s5 = fstr::<8>::new();
846        let ss5 = s5.as_str();
847
848        let mut s6 = fstr::<32>::new();
849        let result = write!(&mut s6, "hello {}, {}, {}", 1, 2, 3);
850        assert_eq!(s6, "hello 1, 2, 3");
851        println!("s6 is {}, result is {:?}", &s6, &result);
852
853        let s7 = str_format!(fstr<32>, "abc {}, {}", 1, 10);
854        println!("s7 is {}", &s7);
855        let s8 = try_format!(fstr<32>, "abcdefg {}, {}", 1, 10);
856        println!("s8 is {}", &s8.unwrap());
857
858        let mut f1 = fstr::<16>::from("abcdefg");
859        let f2 = f1.to_ascii_uppercase();
860        //f1 = f2; // copy?
861
862        #[cfg(feature = "experimental")]
863        {
864            let mut s = <zstr<8>>::from("abcd");
865            s[0] = b'A'; // impls IndexMut for zstr (not for fstr nor strN types)
866            assert_eq!('A', s.nth_ascii(0));
867        }
868
869        use std::collections::HashMap;
870        let mut hm = HashMap::new();
871        hm.insert(str8::from("abc"), 1);
872        assert!(hm.contains_key(&str8::from("abc")));
873
874        let mut a: fstr<8> = fstr::from("abcdef");
875        let rem = a.push("g");
876        assert!(rem == "" && &a == "abcdefg");
877
878        ftests();
879    } //maintest
880
881    #[cfg(feature = "std")]
882    #[cfg(not(feature = "no-alloc"))]
883    fn ftests() {
884        extern crate std;
885        use std::{format, println, string::String};
886        let a: fstr<8> = fstr::from("abcdefg"); //creates fstr from &str
887        let a1: fstr<8> = a; // copied, not moved
888        let a2: &str = a.to_str();
889        let a3: String = a.to_string();
890        assert_eq!(a.nth_ascii(2), 'c');
891        let ab = a.substr(1, 5); // copies substring to new fstr
892        assert!(ab == "bcde" && a1 == a); // can compare with &str and itself
893        assert!(a < ab); // implements Ord trait (and Hash
894        let mut u: fstr<8> = fstr::from("aλb"); //unicode support
895        u.nth(1).map(|x| assert_eq!(x, 'λ')); // nth returns Option<char>
896                                              //for x in u.nth(1) {assert_eq!(x,'λ');} // nth returns Option<char>
897        assert!(u.set(1, 'μ')); // changes a character of the same character class
898        assert!(!u.set(1, 'c')); // .set returns false on failure
899        assert!(u.set(2, 'c'));
900        assert_eq!(u, "aμc");
901        assert_eq!(u.len(), 4); // length in bytes
902        assert_eq!(u.charlen(), 3); // length in chars
903        let mut ac: fstr<16> = a.resize(); // copies to larger capacity string
904        let remainder: &str = ac.push("hijklmnopqrst"); //appends string, returns left over
905        assert_eq!(ac.len(), 16);
906        assert_eq!(remainder, "qrst");
907        ac.truncate(10); // shortens string in place
908        assert_eq!(&ac, "abcdefghij");
909        println!("ac {}, remainder: {}", &ac, &remainder);
910
911        assert_eq!(ac.pop_char().unwrap(), 'j');
912        assert_eq!(ac, "abcdefghi");
913
914        let ac2: fstr<16> = fstr::make("abcd");
915        ac.truncate(4);
916        assert_eq!(ac, ac2);
917
918        let mut z8 = zstr::<16>::from("abc12");
919        let z8o = str_format!(zstr<16>, "xxx {}3", z8);
920        assert_eq!(z8o, "xxx abc123");
921        let zoo = format!("xx{}yy", z8o);
922        assert_eq!(zoo, "xxxxx abc123yy");
923    } //ftr tests
924
925    #[cfg(all(feature = "std", feature = "flex-str"))]
926    #[cfg(not(feature = "no-alloc"))]
927    fn flextest() {
928        extern crate std;
929        use std::fmt::Write;
930        use std::println;
931        use std::string::String;
932        println!("starting Flexstr tests...");
933        let mut a: Flexstr<8> = Flexstr::from("abcdef");
934        a.truncate(5);
935        assert_eq!(a, "abcde"); // can compare equality with &str
936        assert_eq!(&a[..3], "abc"); // impls Index
937        println!("Flexstr slice: {}", &a[1..4]);
938        let ab = Flexstr::<8>::from("bcdefghijklmnop");
939        assert!(a.is_fixed());
940        assert!(!ab.is_fixed());
941        let a2: str8 = a.get_str().unwrap();
942        assert!(a < ab); // impls Ord, (and Hash, Debug, Eq, other common traits)
943        let astr: &str = a.to_str(); // convert to &str (zero copy)
944        let aowned: String = a.to_string(); // convert to owned string
945                                            //let b = a.take_string();
946        let mut u = Flexstr::<8>::from("aλb"); //unicode support
947        assert_eq!(u.nth(1), Some('λ')); // get nth character
948        assert_eq!(u.nth_ascii(3), 'b'); // get nth byte as ascii character
949        assert!(u.set(1, 'μ')); // changes a character of the same character class
950        assert!(!u.set(1, 'c')); // .set returns false on failure
951        assert!(u.set(2, 'c'));
952        assert_eq!(u, "aμc");
953        assert_eq!(u.len(), 4); // length in bytes
954        assert_eq!(u.charlen(), 3); // length in chars
955        let mut v: Flexstr<4> = Flexstr::from("aμcxyz");
956        v.set(1, 'λ');
957        println!("v: {}", &v);
958
959        let mut u2: Flexstr<16> = u.resize();
960        u2.push_str("aaaaaaaa");
961        println!("{} len {}", &u2, u2.len());
962        assert!(u2.is_fixed());
963
964        let mut s: Flexstr<8> = Flexstr::from("abcdef");
965        assert!(s.is_fixed());
966        s.push_str("ghijk");
967        assert!(s.is_owned());
968        s.truncate(7);
969        assert!(s.is_fixed());
970        let ab = Flexstr::<32>::from("bcdefghijklmnop");
971        println!("size of ab: {}", std::mem::size_of::<Flexstr<32>>());
972
973        let mut vv = Flexstr::<8>::from("abcd");
974        vv.push('e');
975        //vv.push('λ');
976        println!("vv: {}", &vv);
977
978        vv.push_str("abcdefasdfasdfadfssfs");
979        let vvs = vv.split_off();
980        println!("vv: {},  vvs: {}", &vv, &vvs);
981
982        let mut fs: Flexstr<4> = Flexstr::from("abcdefg");
983        let extras = fs.split_off();
984        assert!(&fs == "abc" && &extras == "defg" && fs.is_fixed());
985
986        let fss = fs.to_string();
987        assert!(&fss == "abc");
988    } //flextest
989
990    #[cfg(feature = "std")]
991    #[cfg(not(feature = "no-alloc"))]
992    fn tinytests() {
993        extern crate std;
994        use std::fmt::Write;
995        use std::println;
996        use std::string::String;
997        println!("starting tstr tests...");
998        let a: str8 = str8::from("abcdef");
999        let a2 = a; // copied, not moved
1000        let ab = a.substr(1, 5); // copies, not move substring to new string
1001        assert_eq!(ab, "bcde"); // can compare equality with &str
1002        assert_eq!(&a[..3], "abc"); // impls Index
1003        assert_eq!(ab.len(), 4);
1004        println!("str8: {}", &a);
1005        assert!(a < ab); // impls Ord, (and Hash, Debug, Eq, other common traits)
1006        let astr: &str = a.to_str(); // convert to &str (zero copy)
1007        let aowned: String = a.to_string(); // convert to owned string
1008        let afstr: fstr<8> = fstr::from(a); // fstr is another fixedstr crate type
1009        let azstr: zstr<16> = zstr::from(a); // so is zstr
1010        let a32: str32 = a.resize(); // same type of string with 31-byte capacity
1011        let mut u = str8::from("aλb"); //unicode support
1012        assert_eq!(u.nth(1), Some('λ')); // get nth character
1013        assert_eq!(u.nth_ascii(3), 'b'); // get nth byte as ascii character
1014        assert!(u.set(1, 'μ')); // changes a character of the same character class
1015        assert!(!u.set(1, 'c')); // .set returns false on failure
1016        assert!(u.set(2, 'c'));
1017        assert_eq!(u, "aμc");
1018        assert_eq!(u.len(), 4); // length in bytes
1019        assert_eq!(u.charlen(), 3); // length in chars
1020        let mut ac: str16 = a.reallocate().unwrap(); //copies to larger capacity type
1021        let remainder = ac.push("ghijklmnopq"); //append up to capacity, returns remainder
1022        assert_eq!(ac.len(), 15);
1023        assert_eq!(remainder, "pq");
1024        println!("ac {}, remainder: {}", &ac, &remainder);
1025        ac.truncate(9); // keep first 9 chars
1026        assert_eq!(&ac, "abcdefghi");
1027        println!("ac {}, remainder: {}", &ac, &remainder);
1028
1029        let mut s = str8::from("aλc");
1030        assert_eq!(s.capacity(), 7);
1031        assert_eq!(s.push("1234567"), "4567");
1032        assert_eq!(s, "aλc123");
1033        assert_eq!(s.charlen(), 6); // length in chars
1034        assert_eq!(s.len(), 7); // length in bytes
1035
1036        println!("size of str8: {}", std::mem::size_of::<str8>());
1037        println!("size of zstr<8>: {}", std::mem::size_of::<zstr<8>>());
1038        println!("size of &str: {}", std::mem::size_of::<&str>());
1039        println!("size of &str8: {}", std::mem::size_of::<&str8>());
1040
1041        let mut toosmall: fstr<8> = fstr::make("abcdefghijkl");
1042        let mut toosmallz: zstr<8> = zstr::make("abcdefghijkl");
1043        let mut toosmallt: str8 = str8::make("abcdefghijkl");
1044        println!("toosmall: {}", toosmall);
1045        let waytoosmall: fstr<4> = toosmall.resize();
1046        let way2: zstr<4> = toosmallz.resize();
1047        let mut way3: str16 = str16::make("abcdefedefsfsdfsd");
1048        let way4: str8 = way3.resize();
1049        way3 = way4.resize();
1050        println!("way3: {}, length {}", way3, way3.len());
1051
1052        // converting to other fixedstr crate types
1053        let b: str8 = str8::from("abcdefg");
1054        let mut b2: fstr<32> = fstr::from(b);
1055        b2.push("hijklmnop");
1056        println!("b2 is {}", &b2);
1057        let mut b3: zstr<300> = zstr::from(b);
1058        b3.push("hijklmnopqrstuvw");
1059        println!("b3 is {}", &b3);
1060        let mut b4 = str128::from(b2);
1061        b4.push("xyz");
1062        println!("b4 is {}", &b4);
1063
1064        let (upper, lower) = (str8::make("ABC"), str8::make("abc"));
1065        assert_eq!(upper, lower.to_ascii_upper());
1066
1067        let c1 = str8::from("abcdef");
1068        let c2 = str8::from("xyz123");
1069        let c3 = c1 + c2 + "999";
1070        assert_eq!(c3, "abcdefxyz123999");
1071        assert_eq!(c3.capacity(), 15);
1072        //println!("c3 is {}, capacity {}",&c3, &c3.capacity());
1073
1074        let c4 = str_format!(str16, "abc {}{}{}", 1, 2, 3);
1075        assert_eq!(c4, "abc 123");
1076        //    let c4 = str_format!(str16,"abc {}",&c1);
1077        //    println!("c4 is {}",&c4);
1078        //assert_eq!(c4,"abc abcdef");
1079        let c5 = try_format!(str8, "abc {}{}", &c1, &c2);
1080        assert!(c5.is_none());
1081        let s = try_format!(str32, "abcdefg{}", "hijklmnop").unwrap();
1082        let s2 = try_format!(str8, "abcdefg{}", "hijklmnop");
1083        assert!(s2.is_none());
1084
1085        let mut c4b = str16::from("abc 12345");
1086        c4b.truncate(7);
1087        assert_eq!(c4, c4b);
1088
1089        let zb = ztr8::from("abc");
1090        let mut zc = ztr8::from("abcde");
1091        zc.truncate(3);
1092        assert_eq!(zb, zc);
1093    } //tiny tests
1094
1095    #[cfg(feature = "pub-tstr")]
1096    fn consttests() {
1097        let ls = tstr::<{ tstr_limit(258) }>::from("abcd");
1098        assert_eq!(ls.capacity(), 255);
1099    } //consttests
1100} //tests mod