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