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