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