fixedstr_ext/
tiny_internal.rs

1//! This module is intended for internal use only.  Only a few
2//! type aliases are exported.  A tiny string or `tstr<N>`, with N<=256,
3//! is a version of fixed str that represents the best compromise between
4//! memory and runtime efficiency.  Each `tstr<N>` can hold a string of up to
5//! N-1 bytes, with max N=256.  A `tstr<N>` is represented underneath
6//! by a `[u8;N]` with the first byte always representing the length of the
7//! string.  A tstr is not necessarily zero-terminated.
8//! Because currently Rust does not allow conditions on const generics
9//! such as `where N<=256`, this type is not fully exported and one can
10//! only use the type aliases.
11
12#![allow(unused_variables)]
13#![allow(non_snake_case)]
14#![allow(non_camel_case_types)]
15#![allow(unused_parens)]
16#![allow(unused_assignments)]
17#![allow(unused_mut)]
18#![allow(unused_imports)]
19#![allow(dead_code)]
20
21#[cfg(not(feature = "no-alloc"))]
22extern crate alloc;
23
24#[cfg(feature = "std")]
25#[cfg(not(feature = "no-alloc"))]
26extern crate std;
27
28#[cfg(feature = "std")]
29#[cfg(not(feature = "no-alloc"))]
30use crate::fstr;
31
32use crate::zstr;
33use crate::{str12, str128, str16, str192, str24, str256, str32, str4, str48, str64, str8, str96};
34use core::cmp::{min, Ordering};
35use core::ops::{Add, Index, IndexMut, Range, RangeFrom, RangeFull, RangeTo};
36use core::ops::{RangeInclusive, RangeToInclusive};
37
38/// **This structure is normally only accessible through the
39/// public types [str4] through [str256].**  These types alias internal
40/// types [tstr]\<4\> through [tstr]\<256\> respectively.  The purpose here
41/// is to guarantee that the maximum size of the structure does not exceed
42/// 256 bytes for it uses the first byte of a u8 array to hold the length of
43/// the string.  The tstr type can be made directly public with the
44/// **`pub-tstr` option**.
45///
46/// A feature unique to the tstr type aliases is the ability to concatenate
47/// strings by generating higher-capacity types. Concatenating two strN
48/// strings will always generate a strM with M=2*N, for str4 - str128.
49/// ```
50///   # use fixedstr_ext::*;
51///   let a = str8::from("aaaaaa");
52///   let b = str8::from("bbbbbb");
53///   let c = a + b;  // type of c will be str16
54///   assert_eq!(c,"aaaaaabbbbbb");
55///   assert_eq!(c.capacity(), 15);
56/// ```
57/// In contrast, concatenating other string types such as zstr will always
58/// produce strings of the same type and capacity.
59#[derive(Copy, Clone, Eq)]
60pub struct tstr<const N: usize = 256> {
61    chrs: [u8; N],
62} //tstr
63impl<const N: usize> tstr<N> {
64    /// creates a new `tstr<N>` with given &str.  If the length of s exceeds
65    /// N, the extra characters are ignored.  This function is also called by
66    /// several others including [tstr::from].  This function can now handle
67    /// utf8 strings properly.
68    pub fn make(s: &str) -> tstr<N> {
69        let mut chars = [0u8; N];
70        let bytes = s.as_bytes(); // &[u8]
71        let blen = bytes.len();
72        let limit = min(N - 1, blen);
73        chars[1..limit + 1].copy_from_slice(&bytes[..limit]);
74        chars[0] = limit as u8;
75        //if chars[0] == 0 {
76        //    chars[0] = blen as u8;
77        //}
78        tstr { chrs: chars }
79    } //make
80
81    /// alias for [Self::make]
82    pub fn create(s: &str) -> tstr<N> {
83        let mut chars = [0u8; N];
84        let bytes = s.as_bytes();
85        let blen = bytes.len();
86        let limit = min(N - 1, blen);
87        chars[1..limit + 1].copy_from_slice(&bytes[..limit]);
88        chars[0] = limit as u8;
89        //if chars[0] == 0 {
90        //    chars[0] = blen as u8;
91        //}
92        tstr { chrs: chars }
93    } //create
94
95    /// version of make that returns the original string slice in an `Err(_)` if
96    /// truncation is requried, or in an `Ok(_)` if no truncation is required
97    pub fn try_make(s: &str) -> Result<tstr<N>, &str> {
98        if s.len() + 1 > N {
99            Err(s)
100        } else {
101            Ok(tstr::make(s))
102        }
103    }
104
105    /// const constructor, to be called from const contexts.  However, as
106    /// const functions are restricted from using iterators, it's slightly
107    /// better to call the non-const constructors in non-const contexts.
108    /// Truncates automatically.
109    pub const fn const_make(s: &str) -> tstr<N> {
110        let mut t = tstr::<N>::new();
111        let mut len = s.len();
112        if len > N - 1 {
113            len = N - 1;
114        } // fix max length
115        t.chrs[0] = len as u8;
116        let bytes = s.as_bytes();
117        let mut i = 0;
118        while i < len {
119            t.chrs[i + 1] = bytes[i];
120            i += 1;
121        }
122        t
123    } //const_make
124
125    /// Version of `const_make` that does not truncate.
126    /// Additionally, because this operation is meant to be evaluated at
127    /// compile time, N is checked to be at least 1 and at most 256: `None`
128    /// is returned if conditions are violated.
129    pub const fn const_try_make(s: &str) -> Option<tstr<N>> {
130        if N == 0 || N > 256 || s.len() + 1 > N {
131            None
132        } else {
133            Some(tstr::const_make(s))
134        }
135    }
136
137    /// creates an empty string; equivalent to tstr::default() but can
138    /// also be called from a const context.
139    #[inline]
140    pub const fn new() -> tstr<N> {
141        tstr { chrs: [0; N] }
142    }
143
144    /// length of the string in bytes (consistent with [str::len]). This
145    /// is a constant-time operation.
146    #[inline]
147    pub const fn len(&self) -> usize {
148        self.chrs[0] as usize
149    }
150
151    /// returns the number of characters in the string regardless of
152    /// character class.  This is not necessarily a constant-time operation.
153    pub fn charlen(&self) -> usize {
154        self.to_str().chars().count()
155    }
156
157    /// returns maximum capacity in bytes
158    #[inline]
159    pub const fn capacity(&self) -> usize {
160        N - 1
161    }
162
163    /// converts tstr to an alloc::string::string
164    #[cfg(not(feature = "no-alloc"))]
165    pub fn to_string(&self) -> alloc::string::String {
166        alloc::string::String::from(self.to_str())
167    }
168
169    /// returns slice of u8 the array underneath the tstr
170    pub fn as_bytes(&self) -> &[u8] {
171        &self.chrs[1..self.len() + 1]
172    }
173
174    /// returns mutable slice of the u8 array underneath (use with care)
175    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
176        let n = self.len() + 1;
177        &mut self.chrs[1..n]
178    }
179
180    /// converts tstr to &str using [core::str::from_utf8_unchecked]
181    pub fn to_str(&self) -> &str {
182        unsafe { core::str::from_utf8_unchecked(&self.chrs[1..self.len() + 1]) }
183    }
184    /// checked version of [tstr::to_str], but may panic
185    pub fn as_str(&self) -> &str {
186        core::str::from_utf8(&self.chrs[1..self.len() + 1]).unwrap()
187    }
188    /// version of [tstr::as_str] that does not call `unwrap`
189    pub fn as_str_safe(&self) -> Result<&str, core::str::Utf8Error> {
190        core::str::from_utf8(&self.chrs[1..self.len() + 1])
191    }
192
193    /// changes a character at *character position* i to c.  This function
194    /// requires that c is in the same character class (ascii or unicode)
195    /// as the char being replaced.  It never shuffles the bytes underneath.
196    /// The function returns true if the change was successful.
197    pub fn set(&mut self, i: usize, c: char) -> bool {
198        let ref mut cbuf = [0u8; 4];
199        c.encode_utf8(cbuf);
200        let clen = c.len_utf8();
201        if let Some((bi, rc)) = self.to_str().char_indices().nth(i) {
202            if clen == rc.len_utf8() {
203                self.chrs[bi + 1..bi + clen + 1].copy_from_slice(&cbuf[..clen]);
204                //for k in 0..clen {self.chrs[bi+k+1] = cbuf[k];}
205                return true;
206            }
207        }
208        return false;
209    } //set
210
211    /// adds chars to end of current string up to maximum size N of `tstr<N>`,
212    /// returns the portion of the push string that was NOT pushed due to
213    /// capacity, so
214    /// if "" is returned then all characters were pushed successfully.
215    pub fn push<'t>(&mut self, s: &'t str) -> &'t str {
216        self.push_str(s)
217    } //push
218
219    /// alias for [Self::push]
220    pub fn push_str<'t>(&mut self, src: &'t str) -> &'t str {
221        //self.push(s)
222        let srclen = src.len();
223        let slen = self.len();
224        let bytes = &src.as_bytes();
225        let length = core::cmp::min(slen + srclen, N - 1);
226        let remain = if N - 1 >= (slen + srclen) {
227            0
228        } else {
229            (srclen + slen) - N + 1
230        };
231        let mut i = 0;
232        while i < srclen && i + slen + 1 < N {
233            self.chrs[slen + i + 1] = bytes[i];
234            i += 1;
235        } //while
236        self.chrs[0] += i as u8;
237        &src[srclen - remain..]
238    } //push_str
239
240    /// pushes a single character to the end of the string, returning
241    /// true on success.
242    pub fn push_char(&mut self, c: char) -> bool {
243        let clen = c.len_utf8();
244        let slen = self.len();
245        if slen + clen >= N {
246            return false;
247        }
248        let mut buf = [0u8; 4]; // char buffer
249        c.encode_utf8(&mut buf);
250        for i in 0..clen {
251            self.chrs[slen + i + 1] = buf[i];
252        }
253        self.chrs[0] = (slen + clen) as u8;
254        true
255    } // push_char
256
257    /// remove and return last character in string, if it exists
258    pub fn pop_char(&mut self) -> Option<char> {
259        if self.len() == 0 {
260            return None;
261        }
262        let (ci, lastchar) = self.char_indices().last().unwrap();
263        self.chrs[0] = ci as u8;
264        Some(lastchar)
265    } //pop
266
267    /// returns the nth char of the tstr
268    pub fn nth(&self, n: usize) -> Option<char> {
269        self.to_str().chars().nth(n)
270    }
271
272    /// returns the nth byte of the string as a char.  This
273    /// function should only be called, for example, on ascii strings.  It
274    /// is designed to be quicker than [tstr::nth], and does not check array bounds or
275    /// check n against the length of the string. Nor does it check
276    /// if the value returned is a valid character.
277    pub const fn nth_bytechar(&self, n: usize) -> char {
278        self.chrs[n + 1] as char
279    }
280
281    /// alias for [Self::nth_bytechar] (for backwards compatibility)
282    pub const fn nth_ascii(&self, n: usize) -> char {
283        self.chrs[n + 1] as char
284    }
285
286    /// determines if string is an ascii string
287    pub fn is_ascii(&self) -> bool {
288        self.to_str().is_ascii()
289    }
290
291    /// shortens the tstr in-place (mutates).  n indicates the number of
292    /// *characters* to keep in thestring. If n is greater than the
293    /// current character-length ([Self::charlen]) of the string, this operation will have no effect.
294    pub fn truncate(&mut self, n: usize) // n is char position, not binary position
295    {
296        if let Some((bi, c)) = self.to_str().char_indices().nth(n) {
297            self.chrs[0] = bi as u8;
298        }
299    }
300
301    /// truncates string up to *byte* position n.  **Panics** if n is
302    /// not on a character boundary, similar to truncate on owned Strings.
303    pub fn truncate_bytes(&mut self, n: usize) {
304        if (n < self.chrs[0] as usize) {
305            assert!(self.is_char_boundary(n));
306            self.chrs[0] = n as u8;
307        }
308    }
309
310    /// Trims **in-place** trailing ascii whitespaces.  This function
311    /// regards all bytes as single chars.  The operation panics if
312    /// the resulting string does not end on a character boundary.
313    pub fn right_ascii_trim(&mut self) {
314        let mut n = self.chrs[0] as usize;
315        while n > 0 && (self.chrs[n] as char).is_ascii_whitespace() {
316            //self.chrs[n-1] = 0;
317            n -= 1;
318        }
319        assert!(self.is_char_boundary(n));
320        self.chrs[0] = n as u8;
321    } //right_trim
322
323    /// resets string to empty string
324    pub fn clear(&mut self) {
325        self.chrs[0] = 0;
326    }
327
328    /// in-place modification of ascii characters to lower-case. Panics if
329    /// the string is not ascii.
330    pub fn make_ascii_lowercase(&mut self) {
331        assert!(self.is_ascii());
332        let end = (self.chrs[0] as usize) + 1;
333        for b in &mut self.chrs[1..end] {
334            if *b >= 65 && *b <= 90 {
335                *b |= 32;
336            }
337        }
338    } //make_ascii_lowercase
339
340    /// in-place modification of ascii characters to upper-case.  Panics if
341    /// the string is not ascii.
342    pub fn make_ascii_uppercase(&mut self) {
343        assert!(self.is_ascii());
344        let end = (self.chrs[0] as usize) + 1;
345        for b in &mut self.chrs[1..end] {
346            if *b >= 97 && *b <= 122 {
347                *b -= 32;
348            }
349        }
350    }
351
352    /// Constructs a clone of this fstr but with only upper-case ascii
353    /// characters.  This contrasts with [str::to_ascii_uppercase],
354    /// which creates an owned String.
355    pub fn to_ascii_upper(&self) -> Self {
356        let mut cp = self.clone();
357        cp.make_ascii_uppercase();
358        cp
359    }
360
361    /// Constructs a clone of this fstr but with only lower-case ascii
362    /// characters.  This contrasts with [str::to_ascii_lowercase],
363    /// which creates an owned String.
364    pub fn to_ascii_lower(&self) -> Self {
365        let mut cp = *self;
366        cp.make_ascii_lowercase();
367        cp
368    }
369
370    /// Tests for ascii case-insensitive equality with another string.
371    /// This function does not check if either string is ascii.
372    pub fn case_insensitive_eq<TA>(&self, other: TA) -> bool
373    where
374        TA: AsRef<str>,
375    {
376        if self.len() != other.as_ref().len() {
377            return false;
378        }
379        let obytes = other.as_ref().as_bytes();
380        for i in 0..self.len() {
381            let mut c = self.chrs[i + 1];
382            if (c > 64 && c < 91) {
383                c = c | 32;
384            } // make lowercase
385            let mut d = obytes[i];
386            if (d > 64 && d < 91) {
387                d = d | 32;
388            } // make lowercase
389            if c != d {
390                return false;
391            }
392        } //for
393        true
394    } //case_insensitive_eq
395
396    /// Decodes a UTF-16 encodeded slice. If a decoding error is encountered
397    /// or capacity exceeded, an `Err(s)` is returned where s is the
398    /// the encoded string up to the point of the error.
399    pub fn from_utf16(v: &[u16]) -> Result<Self, Self> {
400        let mut s = Self::new();
401        for c in char::decode_utf16(v.iter().cloned()) {
402            if let Ok(c1) = c {
403                if !s.push_char(c1) {
404                    return Err(s);
405                }
406            } else {
407                return Err(s);
408            }
409        }
410        Ok(s)
411    } //from_utf16
412
413    /*
414       // These functions will replace calls to to_str (internal)
415       // ... but both are unsafe because of unwrap
416       #[cfg(not(feature = "prioritize-safety"))]
417       #[inline(always)]
418       fn makestr(&self) -> &str {
419         unsafe { core::str::from_utf8_unchecked(&self.chrs[1..self.len() + 1]) }
420      }
421       #[cfg(feature = "prioritize-safety")]
422       #[inline(always)]
423       fn makestr(&self) -> &str {
424         core::str::from_utf8(&self.chrs[1..self.len() + 1]).unwrap()
425       }
426    */
427} //impl tstr<N>
428  ///////////////////////
429
430impl<const N: usize> core::ops::Deref for tstr<N> {
431    type Target = str;
432    fn deref(&self) -> &Self::Target {
433        self.to_str()
434    }
435}
436
437impl<const N: usize> core::convert::AsRef<str> for tstr<N> {
438    fn as_ref(&self) -> &str {
439        self.to_str()
440    }
441}
442impl<const N: usize> core::convert::AsMut<str> for tstr<N> {
443    fn as_mut(&mut self) -> &mut str {
444        let blen = self.len() + 1;
445        unsafe { core::str::from_utf8_unchecked_mut(&mut self.chrs[1..blen]) }
446    }
447}
448impl<T: AsRef<str> + ?Sized, const N: usize> core::convert::From<&T> for tstr<N> {
449    fn from(s: &T) -> tstr<N> {
450        tstr::make(s.as_ref())
451    }
452}
453impl<T: AsMut<str> + ?Sized, const N: usize> core::convert::From<&mut T> for tstr<N> {
454    fn from(s: &mut T) -> tstr<N> {
455        tstr::make(s.as_mut())
456    }
457}
458
459#[cfg(not(feature = "no-alloc"))]
460impl<const N: usize> core::convert::From<alloc::string::String> for tstr<N> {
461    fn from(s: alloc::string::String) -> tstr<N> {
462        tstr::<N>::make(&s[..])
463    }
464}
465
466#[cfg(feature = "std")]
467#[cfg(not(feature = "no-alloc"))]
468impl<const N: usize, const M: usize> std::convert::From<fstr<M>> for tstr<N> {
469    fn from(s: fstr<M>) -> tstr<N> {
470        tstr::<N>::make(s.to_str())
471    }
472}
473
474impl<const N: usize, const M: usize> core::convert::From<zstr<M>> for tstr<N> {
475    fn from(s: zstr<M>) -> tstr<N> {
476        tstr::<N>::make(s.to_str())
477    }
478}
479
480impl<const N: usize> core::cmp::PartialOrd for tstr<N> {
481    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
482        Some(self.cmp(other))
483    }
484}
485
486impl<const N: usize> core::cmp::Ord for tstr<N> {
487    fn cmp(&self, other: &Self) -> Ordering {
488        self.chrs[1..self.len() + 1].cmp(&other.chrs[1..other.len() + 1])
489    }
490}
491
492impl<const M: usize> tstr<M> {
493    /// converts an tstr\<M\> to an tstr\<N\>. If the length of the string being
494    /// converted is greater than N-1, the extra characters will be ignored.
495    /// This operation produces a copy (non-destructive).
496    /// Example:
497    ///```ignore
498    ///  let s1:tstr<8> = tstr::from("abcdefg");
499    ///  let s2:tstr<16> = s1.resize();
500    ///```
501    pub fn resize<const N: usize>(&self) -> tstr<N> {
502        let slen = self.len();
503        let length = if (slen < N - 1) { slen } else { N - 1 };
504        let mut chars = [0u8; N];
505        chars[1..length + 1].copy_from_slice(&self.chrs[1..length + 1]);
506        //for i in 0..length {chars[i+1] = self.chrs[i+1];}
507        chars[0] = (length) as u8;
508        tstr { chrs: chars }
509    } //resize
510
511    /// version of resize that does not allow string truncation due to length
512    pub fn reallocate<const N: usize>(&self) -> Option<tstr<N>> {
513        if self.len() < N {
514            Some(self.resize())
515        } else {
516            None
517        }
518    } //reallocate
519} //impl tstr<M>
520
521impl<const N: usize> core::fmt::Display for tstr<N> {
522    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
523        //write!(f, "{}", self.to_str())
524        f.pad(self.to_str())
525    }
526}
527
528impl<const N: usize> PartialEq<&str> for tstr<N> {
529    fn eq(&self, other: &&str) -> bool {
530        self.to_str() == *other // see below
531    } //eq
532}
533impl<const N: usize> PartialEq<&str> for &tstr<N> {
534    fn eq(&self, other: &&str) -> bool {
535        &self.to_str() == other
536    } //eq
537}
538impl<'t, const N: usize> PartialEq<tstr<N>> for &'t str {
539    fn eq(&self, other: &tstr<N>) -> bool {
540        &other.to_str() == self
541    }
542}
543impl<'t, const N: usize> PartialEq<&tstr<N>> for &'t str {
544    fn eq(&self, other: &&tstr<N>) -> bool {
545        &other.to_str() == self
546    }
547}
548
549/// defaults to empty string
550impl<const N: usize> Default for tstr<N> {
551    fn default() -> Self {
552        tstr::<N>::new()
553    }
554}
555#[cfg(feature = "std")]
556#[cfg(not(feature = "no-alloc"))]
557impl<const N: usize, const M: usize> PartialEq<tstr<N>> for fstr<M> {
558    fn eq(&self, other: &tstr<N>) -> bool {
559        other.to_str() == self.to_str()
560    }
561}
562#[cfg(feature = "std")]
563#[cfg(not(feature = "no-alloc"))]
564impl<const N: usize, const M: usize> PartialEq<fstr<N>> for tstr<M> {
565    fn eq(&self, other: &fstr<N>) -> bool {
566        other.to_str() == self.to_str()
567    }
568}
569
570impl<const N: usize, const M: usize> PartialEq<zstr<N>> for tstr<M> {
571    fn eq(&self, other: &zstr<N>) -> bool {
572        other.to_str() == self.to_str()
573    }
574}
575
576impl<const N: usize> core::fmt::Debug for tstr<N> {
577    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
578        f.pad(&self.to_str())
579    }
580} // Debug impl
581
582/*
583///Convert tstr to &str slice
584impl<IndexType, const N: usize> core::ops::Index<IndexType> for tstr<N>
585where
586    IndexType: core::slice::SliceIndex<str>,
587{
588    type Output = IndexType::Output;
589    fn index(&self, index: IndexType) -> &Self::Output {
590        &self.to_str()[index]
591    }
592} //impl Index
593*/
594
595impl<const N: usize> tstr<N> {
596    /// returns a copy of the portion of the string, string could be truncated
597    /// if indices are out of range. Similar to slice [start..end]
598    pub fn substr(&self, start: usize, end: usize) -> tstr<N> {
599        let mut chars = [0u8; N];
600        let mut inds = self.char_indices();
601        let len = self.len();
602        if start >= len || end <= start {
603            return tstr { chrs: chars };
604        }
605        chars[0] = (end - start) as u8;
606        let (si, _) = inds.nth(start).unwrap();
607        let last = if (end >= len) {
608            len
609        } else {
610            match inds.nth(end - start - 1) {
611                Some((ei, _)) => ei,
612                None => len,
613            } //match
614        }; //let last =...
615        chars[1..last - si + 1].copy_from_slice(&self.chrs[si + 1..last + 1]);
616        /*
617        for i in si..last
618        {
619          chars[i-si+1] = self.chrs[i+1];
620        }
621        */
622        tstr { chrs: chars }
623    } //substr
624}
625
626impl Add for str8 {
627    type Output = str16;
628    fn add(self, other: Self) -> Self::Output {
629        let mut cat: Self::Output = self.resize();
630        let slen = self.len();
631        let olen = other.len();
632        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
633        cat.chrs[0] = (slen + olen) as u8;
634        cat
635    }
636} //Add
637
638impl Add for str16 {
639    type Output = str32;
640    fn add(self, other: Self) -> Self::Output {
641        let mut cat: Self::Output = self.resize();
642        let slen = self.len();
643        let olen = other.len();
644        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
645        cat.chrs[0] = (slen + olen) as u8;
646        cat
647    }
648} //Add
649
650impl Add for str32 {
651    type Output = str64;
652    fn add(self, other: Self) -> Self::Output {
653        let mut cat: Self::Output = self.resize();
654        let slen = self.len();
655        let olen = other.len();
656        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
657        cat.chrs[0] = (slen + olen) as u8;
658        cat
659    }
660} //Add
661
662impl Add for str64 {
663    type Output = str128;
664    fn add(self, other: Self) -> Self::Output {
665        let mut cat: Self::Output = self.resize();
666        let slen = self.len();
667        let olen = other.len();
668        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
669        cat.chrs[0] = (slen + olen) as u8;
670        cat
671    }
672} //Add
673
674impl Add for str128 {
675    type Output = str256;
676    fn add(self, other: Self) -> Self::Output {
677        let mut cat: Self::Output = self.resize();
678        let slen = self.len();
679        let olen = other.len();
680        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
681        cat.chrs[0] = (slen + olen) as u8;
682        cat
683    }
684} //Add
685
686impl Add for str4 {
687    type Output = str8;
688    fn add(self, other: Self) -> Self::Output {
689        let mut cat: Self::Output = self.resize();
690        let slen = self.len();
691        let olen = other.len();
692        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
693        cat.chrs[0] = (slen + olen) as u8;
694        cat
695    }
696} //Add
697
698impl Add for str12 {
699    type Output = str24;
700    fn add(self, other: Self) -> Self::Output {
701        let mut cat: Self::Output = self.resize();
702        let slen = self.len();
703        let olen = other.len();
704        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
705        cat.chrs[0] = (slen + olen) as u8;
706        cat
707    }
708} //Add
709
710impl Add for str24 {
711    type Output = str48;
712    fn add(self, other: Self) -> Self::Output {
713        let mut cat: Self::Output = self.resize();
714        let slen = self.len();
715        let olen = other.len();
716        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
717        cat.chrs[0] = (slen + olen) as u8;
718        cat
719    }
720} //Add
721
722impl Add for str48 {
723    type Output = str96;
724    fn add(self, other: Self) -> Self::Output {
725        let mut cat: Self::Output = self.resize();
726        let slen = self.len();
727        let olen = other.len();
728        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
729        cat.chrs[0] = (slen + olen) as u8;
730        cat
731    }
732} //Add
733
734impl Add for str96 {
735    type Output = str192;
736    fn add(self, other: Self) -> Self::Output {
737        let mut cat: Self::Output = self.resize();
738        let slen = self.len();
739        let olen = other.len();
740        cat.chrs[slen + 1..slen + olen + 1].copy_from_slice(&other.chrs[1..olen + 1]);
741        cat.chrs[0] = (slen + olen) as u8;
742        cat
743    }
744} //Add
745
746/* conflicting impl
747impl<const N: usize,TA:AsRef<str>> Add<TA> for tstr<N> {
748    type Output = tstr<N>;
749    fn add(self, other: TA) -> tstr<N> {
750        let mut a2 = self;
751        a2.push(other.as_ref());
752        a2
753    }
754} //Add &str
755*/
756
757impl<const N: usize> Add<&str> for tstr<N> {
758    type Output = tstr<N>;
759    fn add(self, other: &str) -> tstr<N> {
760        let mut a2 = self;
761        a2.push(other);
762        a2
763    }
764} //Add &str
765
766impl<const N: usize> Add<&tstr<N>> for &str {
767    type Output = tstr<N>;
768    fn add(self, other: &tstr<N>) -> tstr<N> {
769        let mut a2 = tstr::from(self);
770        a2.push(other);
771        a2
772    }
773} //Add &str on left
774
775impl<const N: usize> Add<tstr<N>> for &str {
776    type Output = tstr<N>;
777    fn add(self, other: tstr<N>) -> tstr<N> {
778        let mut a2 = tstr::from(self);
779        a2.push(&other);
780        a2
781    }
782} //Add &str on left
783
784////////////// core::fmt::Write trait
785/// Usage:
786/// ```
787///   # use fixedstr_ext::*;
788///   use core::fmt::Write;
789///   let mut s = str16::new();
790///   let result = write!(&mut s,"hello {}, {}, {}",1,2,3);
791///   /* or */
792///   let s2 = str_format!(str32,"abx{}{}{}",1,2,3);
793/// ```
794impl<const N: usize> core::fmt::Write for tstr<N> {
795    fn write_str(&mut self, s: &str) -> core::fmt::Result {
796        if s.len() + self.len() + 1 > N {
797            return Err(core::fmt::Error::default());
798        }
799        self.push(s);
800        Ok(())
801    } //write_str
802} //core::fmt::Write trait
803
804impl<const N: usize> core::hash::Hash for tstr<N> {
805    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
806        self.as_ref().hash(state);
807    }
808} //hash
809
810impl<const N: usize> core::cmp::PartialEq for tstr<N> {
811    fn eq(&self, other: &Self) -> bool {
812        self.as_ref() == other.as_ref()
813    }
814}
815
816impl<const N: usize> core::str::FromStr for tstr<N> {
817    type Err = &'static str;
818    fn from_str(s: &str) -> Result<Self, Self::Err> {
819        if N < 257 && s.len() < N {
820            Ok(tstr::from(s))
821        } else {
822            Err("Parse Error: capacity exceeded")
823        }
824    }
825}
826
827/// const function to limit usize value to between 1 and 256.
828/// Can be called when tstr is created (under `pub-tstr` feature):
829/// ```
830///   #[cfg(feature = "pub-tstr")]
831///   # use fixedstr_ext::*;
832///   let ls = tstr::<{tstr_limit(258)}>::from("abcd");
833///   assert_eq!(ls.capacity(),255);
834/// ```
835#[cfg(feature = "pub-tstr")]
836pub const fn tstr_limit(n: usize) -> usize {
837    if n == 0 {
838        1
839    } else if n > 256 {
840        256
841    } else {
842        n
843    }
844} //const limit_size
845
846/*   cannot adopt, because it affects type inference of s1 == s2.resize()
847impl<const N: usize, const M:usize> core::cmp::PartialEq<tstr<M>> for tstr<N> {
848    fn eq(&self, other: &tstr<M>) -> bool {
849       self.as_ref() == other.as_ref()
850    }
851}
852*/