fixedstr/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::*;
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
295//////// Unifying Trait Approach
296#[cfg(feature = "experimental")]
297pub trait Fixedstr<const N:usize> {
298 type Target;
299
300 fn make<TA : AsRef<str>>(s:TA) -> Self::Target;
301
302 fn create<TA : AsRef<str>>(s:TA) -> Self::Target {
303 Self::make(s)
304 } // won't compile
305 fn try_make<TA:AsRef<str>>(s:TA) -> Result<Self::Target,TA> {
306 if s.as_ref().len()<N {Ok(Self::make(s))} else {Err(s)}
307 }
308
309 /*const*/ fn const_make(s:&str) -> Self::Target;
310 /*const*/ fn const_try_make(s:&str) -> Option<Self::Target>;
311
312 fn new() -> Self::Target {
313 Self::make("")
314 }
315
316 /*const*/ fn len(&self) -> usize;
317 fn charlen(&self) -> usize;
318 /*const*/ fn capacity(&self) -> usize { N-1 }
319
320 fn to_str(&self) -> &str;
321 fn as_str(&self) -> &str;
322
323 #[cfg(not(feature = "no-alloc"))]
324 fn to_string(&self) -> alloc::string::String {
325 alloc::string::String::from(self.to_str())
326 }
327
328 fn set(&mut self, i: usize, c: char) -> bool;
329
330 fn push_str<'t>(&mut self, src: &'t str) -> &'t str;
331
332 fn push_char(&mut self, c: char) -> bool;
333
334 fn pop_char(&mut self) -> Option<char>;
335
336 fn nth(&self, n: usize) -> Option<char>;
337
338 fn nth_bytechar(&self, n: usize) -> char;
339
340 fn truncate(&mut self, n: usize);
341
342 fn truncate_bytes(&mut self, n: usize);
343
344 fn truncate_unchecked(&mut self, n: usize);
345
346 fn clear(&mut self);
347
348 fn right_ascii_trim(&mut self);
349
350 fn make_ascii_lowercase(&mut self);
351
352 fn make_ascii_uppercase(&mut self);
353
354 fn case_insensitive_eq<TA:AsRef<str>>(&self, other: TA) -> bool;
355
356 fn is_ascii(&self) -> bool {
357 self.to_str().is_ascii()
358 }
359
360
361}//trait Fixedstr
362
363
364
365
366//#[macro_use]
367//extern crate static_assertions;
368
369
370
371
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::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::*;
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
500#[macro_export]
501/// creates a formated string of given type (by implementing [core::fmt::Write]):
502/// ```
503/// # use fixedstr::*;
504/// let s = str_format!(str8,"abc{}{}{}",1,2,3);
505/// assert_eq!(s,"abc123");
506/// ```
507/// will truncate if capacity exceeded, without warning. See [try_format!]
508/// for version that does not truncate.
509macro_rules! str_format {
510 ($ty_size:ty, $($args:tt)*) => {
511 {use core::fmt::Write;
512 let mut fstr0 = <$ty_size>::new();
513 let res=write!(&mut fstr0, $($args)*);
514 fstr0}
515 };
516}
517
518#[macro_export]
519/// version of [str_format]! that returns an Option of the given type.
520/// ```
521/// # use fixedstr::*;
522/// let s = try_format!(str32,"abcdefg{}","hijklmnop").unwrap();
523/// let s2 = try_format!(str8,"abcdefg{}","hijklmnop");
524/// assert!(s2.is_none());
525/// ```
526macro_rules! try_format {
527 ($ty_size:ty, $($args:tt)*) => {
528 {use core::fmt::Write;
529 let mut fstr0 = <$ty_size>::new();
530 let result = write!(&mut fstr0, $($args)*);
531 if result.is_ok() {Some(fstr0)} else {None}}
532 };
533}
534
535/*
536//////////// to string trait
537pub trait ToTstr<const N: usize> {
538 fn to_tstr(&self) -> tstr<N>;
539}//tostring trait
540*/
541
542#[macro_export]
543/// Macro for converting any expression that implements the Display trait
544/// into the specified type, similar to `to_string` but without necessary
545/// heap allocation. Truncation is automatic and silent. Example:
546///```
547/// # use fixedstr::*;
548/// let fs = to_fixedstr!(str8,-0132*2);
549/// assert_eq!(&fs,"-264");
550///```
551/// For version that does not truncate, use [convert_to_str!].
552macro_rules! to_fixedstr {
553 ($ty_size:ty, $x:expr) => {{
554 use core::fmt::Write;
555 let mut fstr0 = <$ty_size>::new();
556 let res = write!(&mut fstr0, "{}", $x);
557 fstr0
558 }};
559}
560
561#[macro_export]
562/// Version of [to_fixedstr!] that returns None instead of truncating .
563///```
564/// # use fixedstr::*;
565/// let fsopt = convert_to_str!(zstr<16>,0.013128009);
566/// assert!(matches!(fsopt.as_deref(),Some("0.013128009")))
567///```
568macro_rules! convert_to_str {
569 ($ty_size:ty, $x:expr) => {{
570 use core::fmt::Write;
571 let mut fstr0 = <$ty_size>::new();
572 let res = write!(&mut fstr0, "{}", $x);
573 if res.is_ok() {
574 Some(fstr0)
575 } else {
576 None
577 }
578 }};
579}
580
581///////////////////////////////////////////////////// Testing ...
582
583#[cfg(test)]
584mod tests {
585 use super::*;
586 #[test]
587 fn testmain() {
588 nostdtest();
589 ztests();
590
591 #[cfg(feature = "std")]
592 #[cfg(not(feature = "no-alloc"))]
593 maintest();
594 #[cfg(all(feature = "flex-str", feature = "std"))]
595 #[cfg(not(feature = "no-alloc"))]
596 flextest();
597 #[cfg(feature = "std")]
598 #[cfg(not(feature = "no-alloc"))]
599 tinytests();
600 #[cfg(all(feature = "std", feature = "flex-str"))]
601 #[cfg(not(feature = "no-alloc"))]
602 poppingtest();
603 #[cfg(all(feature = "std", feature = "shared-str"))]
604 #[cfg(not(feature = "no-alloc"))]
605 strptrtests();
606 #[cfg(feature = "pub-tstr")]
607 consttests();
608 } //testmain
609
610 #[cfg(feature = "std")]
611 #[cfg(feature = "shared-str")]
612 #[cfg(not(feature = "no-alloc"))]
613 fn strptrtests() {
614 extern crate std;
615 use std::fmt::Write;
616 use std::string::String;
617 let mut a = Sharedstr::<8>::from("abc12");
618 let mut b = a.clone();
619 let mut c = Sharedstr::<8>::from("abc");
620 c.push_str("12");
621 assert!(a == c);
622 assert!(a == "abc12");
623 b.push('3');
624 assert!(a == "abc123");
625 assert!("abc123" == b);
626 } //strptrtests
627
628 /// test struct
629 struct AB(i32, u32);
630 impl core::fmt::Display for AB {
631 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
632 write!(f, "{},{}", self.0, self.1)
633 }
634 }
635
636 #[cfg(all(feature = "std", feature = "flex-str"))]
637 #[cfg(not(feature = "no-alloc"))]
638 fn poppingtest() {
639 extern crate std;
640 use std::println;
641 let mut a = Flexstr::<8>::from("abcdef");
642 assert_eq!(a.pop_char().unwrap(), 'f');
643 println!("a: {}", &a);
644 let a = flexstr16::from("abcd");
645 let c: flexstr16 = &a + "efg";
646 assert_eq!(&c, "abcdefg");
647
648 let mut ab = AB(-5, 22 + 1);
649 let abfs = to_fixedstr!(zstr<16>, &ab);
650 assert_eq!(&abfs, "-5,23");
651 let abfs2 = convert_to_str!(zstr<3>, 10003);
652 assert!(abfs2.is_none());
653 } //poppingtest
654
655 fn nostdtest() {
656 let a: str8 = str8::from("abcdef"); //a str8 can hold up to 7 bytes
657 let a2 = a; // copied, not moved
658 let ab = a.substr(1, 5); // copies substring to new string
659 assert_eq!(ab, "bcde"); // compare for equality with &str
660 assert_eq!(&a[..3], "abc"); // impls Deref<str>
661 assert!(a < ab); // and Ord, Hash, Eq, Debug, Display, other common traits
662 let astr: &str = a.to_str(); // convert to &str
663 let azstr: zstr<16> = zstr::from(a); // so is zstr
664 let mut a32: str32 = a.resize(); // same kind of string but with 31-byte capacity
665 a32 = "abc" + a32;
666 let mut u = str8::from("aλb"); //unicode support
667 assert_eq!(u.nth(1), Some('λ')); // get nth character
668 assert_eq!(u.nth_bytechar(3), 'b'); // get nth byte as ascii character
669 assert!(u.set(1, 'μ')); // changes a character of the same character class
670 assert!(!u.set(1, 'c')); // .set returns false on failure
671 assert!(u.set(2, 'c'));
672 assert_eq!(u, "aμc");
673 assert_eq!(u.len(), 4); // length in bytes
674 assert_eq!(u.charlen(), 3); // length in chars
675 let mut ac: str16 = a.reallocate().unwrap(); //copies to larger capacity type
676 let remainder = ac.push_str("ghijklmnopq"); //append up to capacity, returns remainder
677 assert_eq!(ac.len(), 15);
678 assert_eq!(remainder, "pq");
679 ac.truncate(9); // keep first 9 chars
680 assert_eq!(&ac, "abcdefghi");
681 let (upper, lower) = (str8::make("ABC"), str8::make("abc"));
682 assert_eq!(upper, lower.to_ascii_upper()); // no owned String needed
683
684 let c1 = str8::from("abcd"); // string concatenation with + for strN types
685 let c2 = str8::from("xyz");
686 assert!(c2.case_insensitive_eq("XyZ"));
687 let c2b = str16::from("xYz");
688 assert!(c2.case_insensitive_eq(&c2b));
689 let mut c3 = c1 + c2;
690 assert_eq!(c3, "abcdxyz");
691 assert_eq!(c3.capacity(), 15); // type of c3 is str16
692 c3 = "00" + c3 + "."; // cat with &str on left or right
693 assert_eq!(c3, "00abcdxyz.");
694
695 let c4 = str_format!(str16, "abc {}{}{}", 1, 2, 3); // impls std::fmt::Write
696 assert_eq!(c4, "abc 123"); //str_format! truncates if capacity exceeded
697 let c5 = try_format!(str8, "abcdef{}", "ghijklmn");
698 assert!(c5.is_none()); // try_format! returns None if capacity exceeded
699
700 let fs = to_fixedstr!(str8, -0132);
701 assert_eq!(&fs, "-132");
702
703 // testing for constants
704 const C:str16 = str16::const_make("abcd");
705 //const C:zstr<8> = zstr::const_make("abcd");
706 let xarray = [0u8;C.len()];
707 assert_eq!(C,"abcd");
708 assert_eq!(xarray.len(),4);
709
710 //cstr tests
711 #[cfg(feature = "circular-str")]
712 {
713 use crate::circular_string::*;
714 let mut cb = cstr::<16>::make("abc123");
715 assert!(cb.is_contiguous());
716 cb.push_str("xyz");
717 cb.push_front("9876");
718 assert_eq!(cb.pop_char().unwrap(), 'z');
719 assert_eq!(cb.pop_char_front().unwrap(), '9');
720 cb.push_str_front("000");
721 assert_eq!(cb.len(), 14);
722 assert!(&cb == "000876abc123xy");
723 cb.truncate_left(10);
724 assert_eq!(&cb, "23xy");
725 cb.push_str("ijklmno ");
726 cb.push_char_front(' ');
727 assert!(&cb == " 23xyijklmno ");
728 assert!(!cb.is_contiguous());
729 // cb.trim_left();
730 // assert!(&cb == "23xyijklmno ");
731 // cb.trim_right();
732 cb.trim_whitespaces();
733 assert!("23xyijklmno" == &cb);
734 assert!(&cb < "4abc");
735
736 let mut a = cstr::<8>::make("12345678");
737 assert_eq!(a.len(), 8);
738 a.truncate_front(4);
739 assert_eq!(a.len(), 4);
740 assert!(a.is_contiguous());
741 assert!(&a == "5678");
742 a.push_str("abc");
743 assert!(&a == "5678abc");
744 let mut findopt = a.find_substr("8abc");
745 assert_eq!(findopt.unwrap(), 3);
746 findopt = a.rfind_substr("678abc");
747 assert_eq!(findopt.unwrap(), 1);
748 let mut rem = a.push_str("123456");
749 assert_eq!(rem, "23456");
750 a.truncate_left(4);
751 assert_eq!(&a, "abc1");
752 rem = a.push_front("qrstuvw");
753 assert_eq!(&a, "tuvwabc1");
754 assert_eq!(rem, "qrs");
755 rem = a.push_str("");
756 assert_eq!(&a, "tuvwabc1");
757 assert_eq!(rem, "");
758 a.truncate(5);
759 let mut ba = "123" + a;
760 assert_eq!(ba, "123tuvwa");
761 ba.truncate_left(4);
762 a.truncate_left(1);
763 assert_eq!(a, ba);
764
765 #[cfg(feature = "std")]
766 {
767 let bb = cstr::<8>::from("qgg");
768 extern crate std;
769 use std::collections::HashSet;
770 let mut hh = HashSet::new();
771 hh.insert(bb);
772 assert!(hh.get(&bb).is_some());
773 }
774 } //cstr tests
775 } //nostdtest
776
777 fn ztests() {
778 let a: zstr<8> = zstr::from("abcdefg"); //creates zstr from &str
779 let ab = a.substr(1, 5); // copies, not move substring to new string
780 assert_eq!(ab, "bcde"); // can compare equality with &str
781 assert!(ab.case_insensitive_eq("bCdE"));
782 let mut u: zstr<8> = zstr::from("aλb"); //unicode support
783 assert!(u.set(1, 'μ')); // changes a character of the same character class
784 assert!(!u.set(1, 'c')); // .set returns false on failure
785 assert!(u.set(2, 'c'));
786 assert_eq!(u, "aμc");
787 assert_eq!(u.len(), 4); // length in bytes
788 assert_eq!(u.charlen(), 3); // length in chars
789 let mut ac: zstr<16> = a.resize(); // copies to larger capacity string
790 let remainder = ac.push("hijklmnopqrst"); //appends string, returns left over
791 assert_eq!(ac.len(), 15);
792 assert_eq!(remainder, "pqrst");
793 ac.truncate(10);
794 assert_eq!(&ac, "abcdefghij");
795 //println!("ac {}, remainder: {}, len {}", &ac, &remainder, &ac.len());
796 assert_eq!(ac.len(), 10);
797 ac.pop_char();
798 ac.pop_char();
799 assert_eq!(ac.len(), 8);
800 let mut c4 = str_format!(zstr<16>, "abc {}", 123);
801 assert_eq!(c4, "abc 123");
802 let rem = c4.push_str("123456789abcdef");
803 assert_eq!(c4, "abc 12312345678");
804 assert_eq!(rem, "9abcdef");
805
806 let b = [65u8, 66, 67, 0, 0, 68, 0, 69, 0, 70, 0, 71];
807 let mut bz: zstr<16> = zstr::from_raw(&b);
808 bz.push("abcd \t \n\n");
809 //println!("bz: {}, len {}", &bz, bz.len());
810 bz.right_ascii_trim();
811 bz.reverse_bytes();
812 bz.make_ascii_lowercase();
813 //println!("bz after trim, reverse: {}, len {}", &bz, bz.len());
814 } //ztr tests
815
816 #[cfg(feature = "std")]
817 #[cfg(not(feature = "no-alloc"))]
818 fn maintest() {
819 extern crate std;
820 use std::fmt::Write;
821 use std::println;
822 use std::string::String;
823 let s1: fstr<16> = fstr::from("abc");
824 let mut s2: fstr<8> = fstr::from("and xyz");
825 let s2r = s2.push(" and 1234");
826 println!("s1,s2,s2r,s2.len: {}, {}, {}, {}", s1, &s2, &s2r, s2.len());
827 println!("{}", &s1 == "abc");
828 let s3 = s1; // copied, not moved
829 println!("{}", "abc" == &s1);
830 println!("{}, {} ", s1 == s3, s1 == s2.resize());
831
832 let mut s4: fstr<256> = s3.resize();
833 s4.push("ccccccccccccccccccccccccccccccccccccccccccccccccccccccz");
834 println!("{}, length {}", &s4, s4.len());
835 let mut s5: fstr<32> = s4.resize();
836 println!("{}, length {}", &s5, s5.len());
837 println!("{:?}, length {}", &s5[0..10], s5.len());
838 println!("s2.substr {}", s2.substr(2, 6));
839 println!("{}", s2.substr(2, 6).len());
840 let mut s4: fstr<64> = s1.resize();
841 let owned_string: String = s4.to_string();
842 println!("owned s4: {}", &owned_string);
843 let str_slice: &str = s4.to_str();
844 println!("as &str: {}", &str_slice[0..2]);
845 s4 = s1.resize();
846 let s5 = fstr::<8>::new();
847 let ss5 = s5.as_str();
848
849 let mut s6 = fstr::<32>::new();
850 let result = write!(&mut s6, "hello {}, {}, {}", 1, 2, 3);
851 assert_eq!(s6, "hello 1, 2, 3");
852 println!("s6 is {}, result is {:?}", &s6, &result);
853
854 let s7 = str_format!(fstr<32>, "abc {}, {}", 1, 10);
855 println!("s7 is {}", &s7);
856 let s8 = try_format!(fstr<32>, "abcdefg {}, {}", 1, 10);
857 println!("s8 is {}", &s8.unwrap());
858
859 let mut f1 = fstr::<16>::from("abcdefg");
860 let f2 = f1.to_ascii_uppercase();
861 //f1 = f2; // copy?
862
863 #[cfg(feature = "experimental")]
864 {
865 let mut s = <zstr<8>>::from("abcd");
866 s[0] = b'A'; // impls IndexMut for zstr (not for fstr nor strN types)
867 assert_eq!('A', s.nth_ascii(0));
868 }
869
870 use std::collections::HashMap;
871 let mut hm = HashMap::new();
872 hm.insert(str8::from("abc"), 1);
873 assert!(hm.contains_key(&str8::from("abc")));
874
875 let mut a: fstr<8> = fstr::from("abcdef");
876 let rem = a.push("g");
877 assert!(rem == "" && &a == "abcdefg");
878
879 ftests();
880 } //maintest
881
882 #[cfg(feature = "std")]
883 #[cfg(not(feature = "no-alloc"))]
884 fn ftests() {
885 extern crate std;
886 use std::{println, string::String, format};
887 let a: fstr<8> = fstr::from("abcdefg"); //creates fstr from &str
888 let a1: fstr<8> = a; // copied, not moved
889 let a2: &str = a.to_str();
890 let a3: String = a.to_string();
891 assert_eq!(a.nth_ascii(2), 'c');
892 let ab = a.substr(1, 5); // copies substring to new fstr
893 assert!(ab == "bcde" && a1 == a); // can compare with &str and itself
894 assert!(a < ab); // implements Ord trait (and Hash
895 let mut u: fstr<8> = fstr::from("aλb"); //unicode support
896 u.nth(1).map(|x| assert_eq!(x, 'λ')); // nth returns Option<char>
897 //for x in u.nth(1) {assert_eq!(x,'λ');} // nth returns Option<char>
898 assert!(u.set(1, 'μ')); // changes a character of the same character class
899 assert!(!u.set(1, 'c')); // .set returns false on failure
900 assert!(u.set(2, 'c'));
901 assert_eq!(u, "aμc");
902 assert_eq!(u.len(), 4); // length in bytes
903 assert_eq!(u.charlen(), 3); // length in chars
904 let mut ac: fstr<16> = a.resize(); // copies to larger capacity string
905 let remainder: &str = ac.push("hijklmnopqrst"); //appends string, returns left over
906 assert_eq!(ac.len(), 16);
907 assert_eq!(remainder, "qrst");
908 ac.truncate(10); // shortens string in place
909 assert_eq!(&ac, "abcdefghij");
910 println!("ac {}, remainder: {}", &ac, &remainder);
911
912 assert_eq!(ac.pop_char().unwrap(), 'j');
913 assert_eq!(ac, "abcdefghi");
914
915 let ac2: fstr<16> = fstr::make("abcd");
916 ac.truncate(4);
917 assert_eq!(ac, ac2);
918
919 let mut z8 = zstr::<16>::from("abc12");
920 let z8o = str_format!(zstr<16>,"xxx {}3",z8);
921 assert_eq!(z8o, "xxx abc123");
922 let zoo = format!("xx{}yy",z8o);
923 assert_eq!(zoo,"xxxxx abc123yy");
924 } //ftr tests
925
926 #[cfg(all(feature = "std", feature = "flex-str"))]
927 #[cfg(not(feature = "no-alloc"))]
928 fn flextest() {
929 extern crate std;
930 use std::fmt::Write;
931 use std::println;
932 use std::string::String;
933 println!("starting Flexstr tests...");
934 let mut a: Flexstr<8> = Flexstr::from("abcdef");
935 a.truncate(5);
936 assert_eq!(a, "abcde"); // can compare equality with &str
937 assert_eq!(&a[..3], "abc"); // impls Index
938 println!("Flexstr slice: {}", &a[1..4]);
939 let ab = Flexstr::<8>::from("bcdefghijklmnop");
940 assert!(a.is_fixed());
941 assert!(!ab.is_fixed());
942 let a2: str8 = a.get_str().unwrap();
943 assert!(a < ab); // impls Ord, (and Hash, Debug, Eq, other common traits)
944 let astr: &str = a.to_str(); // convert to &str (zero copy)
945 let aowned: String = a.to_string(); // convert to owned string
946 //let b = a.take_string();
947 let mut u = Flexstr::<8>::from("aλb"); //unicode support
948 assert_eq!(u.nth(1), Some('λ')); // get nth character
949 assert_eq!(u.nth_ascii(3), 'b'); // get nth byte as ascii character
950 assert!(u.set(1, 'μ')); // changes a character of the same character class
951 assert!(!u.set(1, 'c')); // .set returns false on failure
952 assert!(u.set(2, 'c'));
953 assert_eq!(u, "aμc");
954 assert_eq!(u.len(), 4); // length in bytes
955 assert_eq!(u.charlen(), 3); // length in chars
956 let mut v: Flexstr<4> = Flexstr::from("aμcxyz");
957 v.set(1, 'λ');
958 println!("v: {}", &v);
959
960 let mut u2: Flexstr<16> = u.resize();
961 u2.push_str("aaaaaaaa");
962 println!("{} len {}", &u2, u2.len());
963 assert!(u2.is_fixed());
964
965 let mut s: Flexstr<8> = Flexstr::from("abcdef");
966 assert!(s.is_fixed());
967 s.push_str("ghijk");
968 assert!(s.is_owned());
969 s.truncate(7);
970 assert!(s.is_fixed());
971 let ab = Flexstr::<32>::from("bcdefghijklmnop");
972 println!("size of ab: {}", std::mem::size_of::<Flexstr<32>>());
973
974 let mut vv = Flexstr::<8>::from("abcd");
975 vv.push('e');
976 //vv.push('λ');
977 println!("vv: {}", &vv);
978
979 vv.push_str("abcdefasdfasdfadfssfs");
980 let vvs = vv.split_off();
981 println!("vv: {}, vvs: {}", &vv, &vvs);
982
983 let mut fs: Flexstr<4> = Flexstr::from("abcdefg");
984 let extras = fs.split_off();
985 assert!(&fs == "abc" && &extras == "defg" && fs.is_fixed());
986
987 let fss = fs.to_string();
988 assert!(&fss == "abc");
989 } //flextest
990
991 #[cfg(feature = "std")]
992 #[cfg(not(feature = "no-alloc"))]
993 fn tinytests() {
994 extern crate std;
995 use std::fmt::Write;
996 use std::println;
997 use std::string::String;
998 println!("starting tstr tests...");
999 let a: str8 = str8::from("abcdef");
1000 let a2 = a; // copied, not moved
1001 let ab = a.substr(1, 5); // copies, not move substring to new string
1002 assert_eq!(ab, "bcde"); // can compare equality with &str
1003 assert_eq!(&a[..3], "abc"); // impls Index
1004 assert_eq!(ab.len(), 4);
1005 println!("str8: {}", &a);
1006 assert!(a < ab); // impls Ord, (and Hash, Debug, Eq, other common traits)
1007 let astr: &str = a.to_str(); // convert to &str (zero copy)
1008 let aowned: String = a.to_string(); // convert to owned string
1009 let afstr: fstr<8> = fstr::from(a); // fstr is another fixedstr crate type
1010 let azstr: zstr<16> = zstr::from(a); // so is zstr
1011 let a32: str32 = a.resize(); // same type of string with 31-byte capacity
1012 let mut u = str8::from("aλb"); //unicode support
1013 assert_eq!(u.nth(1), Some('λ')); // get nth character
1014 assert_eq!(u.nth_ascii(3), 'b'); // get nth byte as ascii character
1015 assert!(u.set(1, 'μ')); // changes a character of the same character class
1016 assert!(!u.set(1, 'c')); // .set returns false on failure
1017 assert!(u.set(2, 'c'));
1018 assert_eq!(u, "aμc");
1019 assert_eq!(u.len(), 4); // length in bytes
1020 assert_eq!(u.charlen(), 3); // length in chars
1021 let mut ac: str16 = a.reallocate().unwrap(); //copies to larger capacity type
1022 let remainder = ac.push("ghijklmnopq"); //append up to capacity, returns remainder
1023 assert_eq!(ac.len(), 15);
1024 assert_eq!(remainder, "pq");
1025 println!("ac {}, remainder: {}", &ac, &remainder);
1026 ac.truncate(9); // keep first 9 chars
1027 assert_eq!(&ac, "abcdefghi");
1028 println!("ac {}, remainder: {}", &ac, &remainder);
1029
1030 let mut s = str8::from("aλc");
1031 assert_eq!(s.capacity(), 7);
1032 assert_eq!(s.push("1234567"), "4567");
1033 assert_eq!(s, "aλc123");
1034 assert_eq!(s.charlen(), 6); // length in chars
1035 assert_eq!(s.len(), 7); // length in bytes
1036
1037 println!("size of str8: {}", std::mem::size_of::<str8>());
1038 println!("size of zstr<8>: {}", std::mem::size_of::<zstr<8>>());
1039 println!("size of &str: {}", std::mem::size_of::<&str>());
1040 println!("size of &str8: {}", std::mem::size_of::<&str8>());
1041
1042 let mut toosmall: fstr<8> = fstr::make("abcdefghijkl");
1043 let mut toosmallz: zstr<8> = zstr::make("abcdefghijkl");
1044 let mut toosmallt: str8 = str8::make("abcdefghijkl");
1045 println!("toosmall: {}", toosmall);
1046 let waytoosmall: fstr<4> = toosmall.resize();
1047 let way2: zstr<4> = toosmallz.resize();
1048 let mut way3: str16 = str16::make("abcdefedefsfsdfsd");
1049 let way4: str8 = way3.resize();
1050 way3 = way4.resize();
1051 println!("way3: {}, length {}", way3, way3.len());
1052
1053 // converting to other fixedstr crate types
1054 let b: str8 = str8::from("abcdefg");
1055 let mut b2: fstr<32> = fstr::from(b);
1056 b2.push("hijklmnop");
1057 println!("b2 is {}", &b2);
1058 let mut b3: zstr<300> = zstr::from(b);
1059 b3.push("hijklmnopqrstuvw");
1060 println!("b3 is {}", &b3);
1061 let mut b4 = str128::from(b2);
1062 b4.push("xyz");
1063 println!("b4 is {}", &b4);
1064
1065 let (upper, lower) = (str8::make("ABC"), str8::make("abc"));
1066 assert_eq!(upper, lower.to_ascii_upper());
1067
1068 let c1 = str8::from("abcdef");
1069 let c2 = str8::from("xyz123");
1070 let c3 = c1 + c2 + "999";
1071 assert_eq!(c3, "abcdefxyz123999");
1072 assert_eq!(c3.capacity(), 15);
1073 //println!("c3 is {}, capacity {}",&c3, &c3.capacity());
1074
1075 let c4 = str_format!(str16, "abc {}{}{}", 1, 2, 3);
1076 assert_eq!(c4, "abc 123");
1077 // let c4 = str_format!(str16,"abc {}",&c1);
1078 // println!("c4 is {}",&c4);
1079 //assert_eq!(c4,"abc abcdef");
1080 let c5 = try_format!(str8, "abc {}{}", &c1, &c2);
1081 assert!(c5.is_none());
1082 let s = try_format!(str32, "abcdefg{}", "hijklmnop").unwrap();
1083 let s2 = try_format!(str8, "abcdefg{}", "hijklmnop");
1084 assert!(s2.is_none());
1085
1086 let mut c4b = str16::from("abc 12345");
1087 c4b.truncate(7);
1088 assert_eq!(c4, c4b);
1089
1090 let zb = ztr8::from("abc");
1091 let mut zc = ztr8::from("abcde");
1092 zc.truncate(3);
1093 assert_eq!(zb, zc);
1094 } //tiny tests
1095
1096 #[cfg(feature = "pub-tstr")]
1097 fn consttests() {
1098 let ls = tstr::<{tstr_limit(258)}>::from("abcd");
1099 assert_eq!(ls.capacity(),255);
1100 }//consttests
1101} //tests mod