fixedstr/
zero_terminated.rs

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