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}