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