fixedstr/
circular_string.rs

1#![allow(unused_variables)]
2#![allow(non_snake_case)]
3#![allow(non_camel_case_types)]
4#![allow(unused_parens)]
5#![allow(unused_assignments)]
6#![allow(unused_mut)]
7#![allow(unused_imports)]
8#![allow(dead_code)]
9//! fixed strings with circular-queue backing
10
11use core::cmp::{min, Ordering, PartialOrd};
12#[cfg(not(feature = "no-alloc"))]
13extern crate alloc;
14use core::ops::Add;
15
16/// **This type is only available with the `circular-str` option.**
17/// A *circular string* is represented underneath by a fixed-size u8
18/// array arranged as a circular queue. The string can wrap around
19/// either end and thus become internally non-contiguous.
20/// This allows for efficient implementations of operations such as
21/// push, trim *in front* of the string.  However, `Deref<str>` is not
22/// implemented as it cannot be done efficiently.  Instead, the
23/// [cstr::to_strs] function returns a pair of string slices, the second
24/// of which is non-empty if the string is not contiguous.  Additionally,
25/// only single-byte characters are currently allowed, although this might
26/// change in the future by using a "ghost vector" at the end of the array.
27/// An iterator [cstr::chars] is provided over all single-byte chars, which
28/// also forms the foundation of other traits such as Eq, Ord, Hash, etc.
29/// The Serialization (serde) and no-std options are both supported.
30///
31/// Each `cstr<N>` can hold up to N bytes and the maximum N is 65535.
32/// **Values of N that are exact powers of 2 are recommended** to speed up
33/// the `%` operation for computing indices in a ciruclar queue.
34///
35/// Examples:
36/// ```
37///  # use fixedstr::*;
38///  let mut cb = cstr::<16>::make("abc123");
39///  cb.push_str("xyz");
40///  cb.push_front("9876");
41///  assert_eq!(cb.pop_char().unwrap(), 'z');
42///  assert_eq!(cb.pop_char_front().unwrap(), '9');
43///  cb.push_str_front("000");
44///  assert_eq!(cb.len(),14);
45///  assert!(&cb == "000876abc123xy");
46///  cb.truncate_left(10);
47///  assert_eq!(&cb,"23xy");
48///  cb.push_str("ijklmno  ");
49///  cb.push_char_front(' ');
50///  assert!(!cb.is_contiguous());
51///  cb.trim_whitespaces();
52///  assert!("23xyijklmno" == &cb);
53///  assert!(&cb < "4abc");   // Ord trait
54///  let mut a = cstr8::from("abc");
55///  let ba:cstr8 = "123" + a; // concat &str on the left efficiently
56///  assert_eq!(ba,"123abc");
57/// ```
58#[derive(Copy, Clone)]
59pub struct cstr<const N: usize = 32> {
60    chrs: [u8; N],
61    front: u16,
62    len: u16,
63} //cstr
64
65impl<const N: usize> cstr<N> {
66    /// create `cstr` from `&str` with silent truncation; panics if
67    /// N is greater than 65535
68    pub fn make(src: &str) -> cstr<N> {
69        if N < 1 || N > 65535 {
70            panic!("cstr strings are limited to a capacity between 1 and 65535");
71        }
72        let mut m = cstr::<N>::new();
73        let length = core::cmp::min(N, src.len());
74        m.chrs[..length].copy_from_slice(&src.as_bytes()[..length]);
75        m.len = length as u16;
76        m
77    } //make
78
79    /// version of make that also panics if the input string is not ascii.
80    pub fn from_ascii(src: &str) -> cstr<N> {
81        if N < 1 || N > 65535 {
82            panic!("cstr strings are limited to a maximum capacity of 65535");
83        }
84        if !src.is_ascii() {
85            panic!("cstr string is not ascii");
86        }
87        let mut m = cstr::<N>::new();
88        let length = core::cmp::min(N, src.len());
89        m.chrs[..length].copy_from_slice(&src.as_bytes()[..length]);
90        m.len = length as u16;
91        m
92    } //from_ascii
93
94    /// version of make that does not truncate: returns original str slice
95    /// as error.  Also checks if N is no greater than 65535 without panic.
96    pub fn try_make(src: &str) -> Result<cstr<N>, &str> {
97        let length = src.len();
98        if length > N || N > 65535 || N < 1 {
99            return Err(src);
100        }
101        let mut m = cstr::new();
102        m.chrs[..].copy_from_slice(&src.as_bytes()[..length]);
103        m.len = length as u16;
104        Ok(m)
105    } //try_make
106
107    /// version of `try_make` that also checks if the input string is ascii.
108    pub fn try_make_ascii(src: &str) -> Option<cstr<N>> {
109        let length = src.len();
110        if length > N || N > 65535 || N < 1 || !src.is_ascii() {
111            return None;
112        }
113        let mut m = cstr::new();
114        m.chrs[..].copy_from_slice(&src.as_bytes()[..length]);
115        m.len = length as u16;
116        Some(m)
117    } //try_make
118
119    /// version of make that returns a pair consisting of the made
120    /// `cstr` and the remainder `&str` that was truncated; panics if
121    /// N is greater than 65535 (but does not check for ascii strings)
122    pub fn make_remainder(src: &str) -> (cstr<N>, &str) {
123        if N > 65535 || N < 1 {
124            panic!("cstr strings are limited to a capacity between 1 and 65535");
125        }
126        let mut m = cstr::new();
127        let length = core::cmp::min(N, src.len());
128        m.chrs[..].copy_from_slice(&src.as_bytes()[..length]);
129        m.len = length as u16;
130        (m, &src[length..])
131    } //try_make
132
133    /// make from a pair of str slices, does not truncate, and checks that
134    /// N is not greater than 65535 without panic.  The returned cstr will
135    /// be contiguous.
136    pub fn from_pair(left: &str, right: &str) -> Option<cstr<N>> {
137        let (llen, rlen) = (left.len(), right.len());
138        if llen + rlen > N || N > 65535 || N < 1 {
139            return None;
140        }
141        let mut m = cstr::new();
142        m.len = (llen + rlen) as u16;
143        m.chrs[..llen].copy_from_slice(&left.as_bytes()[..llen]);
144        m.chrs[llen..].copy_from_slice(&right.as_bytes()[llen..]);
145        Some(m)
146    } //from_pair
147
148/// const constructor, to be called from const contexts.  However, as
149/// const constructors are restricted from using iterators, it's slightly
150/// better to call the non-const constructors in non-const contexts.
151/// Truncates automatically.
152    pub const fn const_make(src: &str) -> cstr<N> {
153        let mut m = cstr::<N>::new();
154        let mut len = src.len();
155        if len>N {len=N;}
156        //m.chrs[..length].copy_from_slice(&src.as_bytes()[..length]);
157        let bytes = src.as_bytes();
158        let mut i = 0;
159        while i<len {
160          m.chrs[i] = bytes[i];
161          i += 1;
162        }
163        m.len = len as u16;
164        m
165    }// const_make
166
167    /// version of `const_make` that does not truncate.
168    pub const fn const_try_make(s:&str) ->  Result<cstr<N>, &str> {
169      if s.len()>N || N<1 || N>65535 {Err(s)}
170      else { Ok(cstr::const_make(s)) }
171    }
172
173    /// checks if the underlying representation of the string is contiguous
174    /// (without wraparound).
175    #[inline(always)]
176    pub fn is_contiguous(&self) -> bool {
177        (self.front as usize + self.len as usize) <= N
178    }
179
180    /// resets the internal representation of the cstr so that it is
181    /// represented contiguously, without wraparound. **Calling this function
182    /// has O(n) cost both in terms of speed and memory** as
183    /// it requires a secondary buffer as well as copying.**
184    pub fn reset(&mut self) {
185        if self.front == 0 {
186            return;
187        }
188        let mut mhrs = [0; N];
189        for i in 0..self.len as usize {
190            mhrs[i] = self.chrs[self.index(i)];
191        }
192        self.chrs = mhrs;
193        self.front = 0;
194    } //reset
195
196    /// clears string to empty string
197    pub fn clear(&mut self) {
198        self.len = 0;
199    }
200
201    /// resets string to empty string and clears underlying buffer to contain
202    /// all zeros.
203    pub fn zero(&mut self) {
204        self.chrs = [0; N];
205        self.front = 0;
206        self.len = 0;
207    }
208
209    /// guarantees a contiguous underlying representation of the string.
210    /// This is a worst-case O(n) operation.
211    pub fn make_contiguous(&mut self) {
212        if !self.is_contiguous() {
213            self.reset();
214        }
215    }
216
217    /// returns the nth char of the fstr.  Since only single-byte characters
218    /// are currently supported by the cstr type, this function is the same
219    /// as [Self::nth_bytechar] except that n is checked against the length of the
220    /// string.
221    pub fn nth(&self, n: usize) -> Option<char> {
222        if n < self.len as usize {
223            Some(self.chrs[self.index(n)] as char)
224        } else {
225            None
226        }
227    }
228
229    /// returns the nth byte of the string as a char, does not check n
230    /// against length of array
231    #[inline]
232    pub const fn nth_bytechar(&self, n: usize) -> char {
233        self.chrs[self.index(n)] as char
234    }
235
236    /// sets the nth byte of the string to the supplied character.
237    /// the character must fit in a single byte.  Returns true on success.
238    pub fn set(&mut self, n: usize, c: char) -> bool {
239        if c.len_utf8() > 1 || n >= self.len as usize {
240            false
241        } else {
242            self.chrs[self.index(n)] = c as u8;
243            true
244        }
245    } //set
246
247    /// pushes given string to the end of the string, returns remainder
248    pub fn push_str<'t>(&mut self, src: &'t str) -> &'t str {
249        let srclen = src.len();
250        let slen = self.len as usize;
251        let bytes = &src.as_bytes();
252        let length = core::cmp::min(slen + srclen, N);
253        let remain = if N > (slen + srclen) {
254            0
255        } else {
256            (srclen + slen) - N
257        };
258        let mut i = 0;
259        while i < srclen && i + slen < N {
260            self.chrs[self.index(slen + i)] = bytes[i];
261            i += 1;
262        } //while
263        self.len += i as u16;
264        &src[srclen - remain..]
265    } //push_str
266
267    /// Pushes string to the **front** of the string, returns remainder.
268    /// because of the circular-queue backing, this operation has the same
269    /// cost as pushing to the back of the string ([Self::push_str]).
270    /// This function does not check if the input string is ascii.
271    pub fn push_front<'t>(&mut self, src: &'t str) -> &'t str {
272        let srclen = src.len();
273        let slen = self.len as usize;
274        let bytes = &src.as_bytes();
275        let length = core::cmp::min(slen + srclen, N);
276        let remain = if N >= (slen + srclen) {
277            0
278        } else {
279            (srclen + slen) - N
280        };
281        let mut i = 0;
282        while i < srclen && i + slen < N {
283            //self.front =(self.front + (N as u16) -1) % (N as u16);
284            self.front = ((self.front as usize + N - 1) % N) as u16;
285            self.chrs[self.front as usize] = bytes[srclen - 1 - i];
286            i += 1;
287        } //while
288        self.len += i as u16;
289        &src[..remain]
290    } //push_front
291
292    /// alias for [Self::push_front]
293    pub fn push_str_front<'t>(&mut self, src: &'t str) -> &'t str {
294        self.push_front(src)
295    }
296
297    /// Pushes a single character to the end of the string, returning
298    /// true on success.  This function checks if the given character
299    /// occupies a single-byte.
300    pub fn push_char(&mut self, c: char) -> bool {
301        let clen = c.len_utf8();
302        if clen > 1 || self.len as usize + clen > N {
303            return false;
304        }
305        let mut buf = [0u8; 4]; // char buffer
306        let bstr = c.encode_utf8(&mut buf);
307        self.push_str(bstr);
308        true
309    } // push_char
310
311    /// Pushes a single character to the front of the string, returning
312    /// true on success.  This function checks if the given character
313    /// occupies a single-byte.
314    pub fn push_char_front(&mut self, c: char) -> bool {
315        let clen = c.len_utf8();
316        if clen > 1 || self.len as usize + clen > N {
317            return false;
318        }
319        let newfront = (self.front as usize + N - 1) % N;
320        self.chrs[newfront] = c as u8;
321        self.front = newfront as u16;
322        self.len += 1;
323        true
324    } //push_char_front
325
326    /// remove and return last character in string, if it exists
327    pub fn pop_char(&mut self) -> Option<char> {
328        if self.len() == 0 {
329            return None;
330        }
331        let lasti = ((self.front + self.len - 1) as usize) % N;
332        let firstchar = self.chrs[lasti] as char;
333        self.len -= 1;
334        Some(firstchar)
335        /*
336        let (l,r) = self.to_strs();
337        let right = if r.len()>0 {r} else {l};
338        let (ci,lastchar) = right.char_indices().last().unwrap();
339        self.len = if r.len()>0 {(l.len() + ci) as u16} else {ci as u16};
340        Some(lastchar)
341        */
342    } //pop
343
344    /// remove and return first character in string, if it exists
345    pub fn pop_char_front(&mut self) -> Option<char> {
346        if self.len() == 0 {
347            return None;
348        }
349        let firstchar = self.chrs[self.front as usize] as char;
350        self.front = self.index16(1);
351        self.len -= 1;
352        Some(firstchar)
353    } //pop_char_front
354
355    /// alias for [Self::truncate]
356    pub fn truncate_right(&mut self, n: usize) {
357        if (n < self.len as usize) {
358            self.len = n as u16;
359        }
360    }
361
362    /// right-truncates string up to byte position n. Only the first
363    /// n bytes will be kept.
364    /// No effect if n is greater than or equal to the length of the string.
365    #[inline]
366    pub fn truncate(&mut self, n: usize) {
367        self.truncate_right(n);
368    }
369
370    /// left-truncates string up to byte position n: that is, the first
371    /// n bytes will be truncated.  Because of the circular queue backing,
372    /// this is an O(1) operation.
373    /// No effect if n is greater than the length of the string.
374    pub fn truncate_left(&mut self, n: usize) {
375        if (n > 0 && n <= self.len as usize) {
376            /*
377            let (a,b) = self.to_strs();
378            if n<a.len() {
379              assert!(a.is_char_boundary(n));
380            }
381            else {
382              assert!(b.is_char_boundary(n-a.len()));
383            }
384            */
385            self.front = self.index16(n as u16);
386            self.len -= n as u16;
387        }
388    } //truncate_left
389
390    /// alias for `truncate_left`
391    pub fn truncate_front(&mut self, n: usize) {
392        self.truncate_left(n);
393    }
394
395    /// finds the position of first character that satisfies given predicate
396    pub fn find<P>(&self, predicate: P) -> Option<usize>
397    where
398        P: Fn(char) -> bool,
399    {
400        let (a, b) = self.to_strs();
401        if let Some(pos) = a.find(|x: char| predicate(x)) {
402            Some(pos)
403        } else if let Some(pos) = b.find(|x: char| predicate(x)) {
404            Some(a.len() + pos)
405        } else {
406            None
407        }
408    } //find
409
410    /// finds the position of last character that satisfies given predicate
411    pub fn rfind<P>(&self, predicate: P) -> Option<usize>
412    where
413        P: Fn(char) -> bool,
414    {
415        let (a, b) = self.to_strs();
416        if let Some(pos) = b.find(|x: char| predicate(x)) {
417            Some(a.len() + pos)
418        } else if let Some(pos) = a.find(|x: char| predicate(x)) {
419            Some(pos)
420        } else {
421            None
422        }
423    } //find
424
425    /// finds position of first matching substring
426    pub fn find_substr(&self, s: &str) -> Option<usize> {
427        let (a, b) = self.to_strs();
428        if let Some(pos) = a.find(s) {
429            return Some(pos);
430        }
431        if s.len() > 1 {
432            //check middle
433            for i in 0..s.len() - 1 {
434                let mid = s.len() - i - 1;
435                if a.ends_with(&s[..mid]) && b.starts_with(&s[mid..]) {
436                    return Some(a.len() - mid);
437                }
438            } // for each intermediate position
439        }
440        if let Some(pos) = b.find(s) {
441            return Some(a.len() + pos);
442        } else {
443            None
444        }
445    } //find_substr
446
447    /// finds position of last matching substring
448    pub fn rfind_substr(&self, s: &str) -> Option<usize> {
449        let (a, b) = self.to_strs();
450        if let Some(pos) = b.find(s) {
451            return Some(a.len() + pos);
452        }
453        if s.len() > 1 {
454            // check middle
455            for i in 0..s.len() - 1 {
456                let mid = s.len() - i - 1;
457                if b.starts_with(&s[mid..]) && a.ends_with(&s[..mid]) {
458                    return Some(a.len() - mid);
459                }
460            } //for
461        }
462        if let Some(pos) = a.find(s) {
463            Some(pos)
464        } else {
465            None
466        }
467    } //find_substr
468
469    /// **in-place** trimming of white spaces at the front of the string
470    pub fn trim_left(&mut self) {
471        let (a, b) = self.to_strs();
472        let offset;
473        if let Some(i) = a.find(|c: char| !c.is_whitespace()) {
474            offset = i as u16;
475        } else if let Some(k) = b.find(|c: char| !c.is_whitespace()) {
476            offset = (a.len() + k) as u16;
477        } else {
478            offset = (a.len() + b.len()) as u16;
479        }
480        self.front = self.index16(offset); //((self.front as usize + offset)%N) as u16;
481        self.len -= offset;
482    } //trim_left
483
484    /// **in-place** trimming of white spaces at the end of the string
485    pub fn trim_right(&mut self) {
486        let (a, b) = self.to_strs();
487        let offset;
488        if b.len() == 0 {
489            if let Some(k) = a.rfind(|c: char| !c.is_whitespace()) {
490                offset = a.len() - k - 1;
491            } else {
492                offset = a.len();
493            }
494        }
495        //contiguous
496        else if let Some(i) = b.rfind(|c: char| !c.is_whitespace()) {
497            offset = b.len() - i - 1;
498        } else if let Some(k) = a.rfind(|c: char| !c.is_whitespace()) {
499            offset = b.len() + (a.len() - k - 1)
500        } else {
501            offset = a.len() + b.len();
502        }
503        self.len -= offset as u16;
504    } //trim_right
505
506    /// **in-place** trimming of white spaces at either end of the string
507    pub fn trim_whitespaces(&mut self) {
508        self.trim_left();
509        self.trim_right();
510    }
511
512    // convenience
513    #[inline(always)]
514    const fn endi(&self) -> usize {
515        // index of last value plus 1
516        //fastmod(self.front as usize + self.len as usize,N)
517        (self.front as usize + self.len as usize) % N
518    } // last
519
520    #[inline(always)]
521    const fn index(&self, i: usize) -> usize {
522        (self.front as usize + i) % N
523    } // index of ith vale
524
525    /// length of string in bytes
526    #[inline(always)]
527    pub const fn len(&self) -> usize {
528        self.len as usize
529    }
530
531    /// construct new, empty string (same as `cstr::default`)
532    #[inline(always)]
533    pub const fn new() -> Self {
534        cstr {
535            chrs: [0; N],
536            front: 0,
537            len: 0,
538        }
539    } //new
540
541    /// returns a pair of string slices `(left,right)` which, when concatenated,
542    /// will yield an equivalent string underneath.  In case of no wraparound,
543    /// the right str will be empty.
544    pub fn to_strs(&self) -> (&str, &str) {
545        let answer;
546        if self.len() == 0 {
547            answer = ("", "");
548        } else if self.is_contiguous() {
549            let front = self.front as usize;
550            answer = (
551                core::str::from_utf8(&self.chrs[front..front + (self.len as usize)]).unwrap(),
552                "",
553            )
554        } else {
555            answer = (
556                core::str::from_utf8(&self.chrs[self.front as usize..]).unwrap(),
557                core::str::from_utf8(&self.chrs[..self.endi()]).unwrap(),
558            )
559        }
560        answer
561    } //to_strs
562
563    /// returns iterator over the characters of the string
564    pub fn chars<'a>(&'a self) -> CircCharIter<'a> {
565        let contig = self.is_contiguous();
566        CircCharIter {
567            first: if contig {
568                &self.chrs[self.front as usize..(self.front + self.len) as usize]
569            } else {
570                &self.chrs[self.front as usize..]
571            },
572            second: if contig {
573                &[]
574            } else {
575                &self.chrs[..self.endi()]
576            },
577            index: 0,
578        }
579    } //chars
580
581    /// alias for [Self.chars]
582    pub fn iter<'a>(&'a self) -> CircCharIter<'a> {
583        self.chars()
584    }
585
586    /// returns a copy of the same string that is contiguous underneath.
587    /// This may call [cstr::reset], which is an O(n) operation.
588    pub fn to_contiguous(&self) -> cstr<N> {
589        let mut c = *self;
590        if !c.is_contiguous() {
591            c.reset();
592        }
593        c
594    }
595
596    /// returns a single str slice if the cstr is contiguous underneath,
597    /// otherwise panics.
598    pub fn force_str(&self) -> &str {
599        let (a, b) = self.to_strs();
600        if b.len() > 0 {
601            panic!("cstr cannot be transformed into a single str slice without calling reset()");
602        }
603        a
604    }
605
606    /// converts cstr to an owned string
607    #[cfg(not(feature = "no-alloc"))]
608    pub fn to_string(&self) -> alloc::string::String {
609        let (a, b) = self.to_strs();
610        let mut s = alloc::string::String::from(a);
611        if b.len() > 0 {
612            s.push_str(b);
613        }
614        s
615    } //to_string
616
617    /// returns a copy of the portion of the string.  Will return empty
618    /// string if indices are invalid. The returned string will be contiguous.
619    pub fn substr(&self, start: usize, end: usize) -> cstr<N> {
620        let mut s = cstr::<N>::default();
621        if (end <= start || start as u16 > self.len - 1 || end > self.len as usize) {
622            return s;
623        }
624        for i in start..end {
625            s.chrs[i - start] = self.chrs[self.index(i)];
626        }
627        s.len = (end - start) as u16;
628        s
629    } //substr
630
631    /// in-place modification of ascii characters to lower-case.
632    pub fn make_ascii_lowercase(&mut self) {
633        for i in 0..self.len as usize {
634            let b = &mut self.chrs[self.index(i)];
635            if *b >= 65 && *b <= 90 {
636                *b |= 32;
637            }
638        }
639    } //make_ascii_lowercase
640
641    /// in-place modification of ascii characters to upper-case.
642    pub fn make_ascii_uppercase(&mut self) {
643        for i in 0..self.len as usize {
644            let b = &mut self.chrs[self.index(i)];
645            if *b >= 97 && *b <= 122 {
646                *b -= 32;
647            }
648        }
649    } //make_ascii_uppercase
650
651    /// Tests for ascii case-insensitive equality with another string.
652    /// This function does not check if the argument is ascii.
653    pub fn case_insensitive_eq<TA>(&self, other: TA) -> bool
654    where
655        TA: AsRef<str>,
656    {
657        if self.len() != other.as_ref().len() {
658            return false;
659        }
660        let obytes = other.as_ref().as_bytes();
661        for i in 0..self.len() {
662            let mut c = self.chrs[(self.front as usize + i) % N];
663            if (c > 64 && c < 91) {
664                c = c | 32;
665            } // make lowercase
666            let mut d = obytes[i];
667            if (d > 64 && d < 91) {
668                d = d | 32;
669            } // make lowercase
670            if c != d {
671                return false;
672            }
673        } //for
674        true
675    } //case_insensitive_eq
676
677    /// Decodes a UTF-16 encodeded slice. If a decoding error is encountered
678    /// or capacity exceeded, an `Err(s)` is returned where s is the
679    /// the encoded string up to the point of the error.  The returned
680    /// string will be contiguous.
681    pub fn from_utf16(v: &[u16]) -> Result<Self, Self> {
682        let mut s = Self::new();
683        for c in char::decode_utf16(v.iter().cloned()) {
684            if let Ok(c1) = c {
685                if !s.push_char(c1) {
686                    return Err(s);
687                }
688            } else {
689                return Err(s);
690            }
691        }
692        Ok(s)
693    } //from_utf16
694
695    /*
696    /// returns an str slice representation by possibly calling
697    /// [Self::reset] first, which is expensive.
698    pub fn force_str(&mut self) -> &str {
699      if !self.is_contiguous() {self.reset();}
700      let(a,_) = self.to_strs();
701      a
702    }
703
704    #[cfg(feature="serde")]
705    /// for serde only, panics if underlying representation is not contiguous
706    pub fn as_str(&self) -> &str {
707      let(a,b) = self.to_strs();
708      if b.len()>0 {panic!("serialization of cstr is only allowed after reset()");}
709      a
710    }
711
712    #[inline]
713    fn index_of(&self,i:usize) -> usize {
714      fastmod(self.front as usize + i,N)
715    }
716    */
717
718    #[inline(always)]
719    fn index16(&self, i: u16) -> u16 {
720        (self.front + i) % (N as u16)
721        //let n = N as u16;
722        //let mask = n-1;
723        //if n&mask==0 { (self.front+i) & mask } else {(self.front+i) % n }
724    }
725} //main impl
726  ///////////////////////////////////////////////////////////////
727
728impl<const M: usize> cstr<M> {
729    /// converts an `cstr<M>` to an `cstr<N>`. If the length of the string being
730    /// converted is greater than N, the extra characters are ignored.
731    /// This operation produces a new string that is contiguous underneath.
732    pub fn resize<const N: usize>(&self) -> cstr<N> {
733        let slen = self.len();
734        let length = if (slen < N) { slen } else { N };
735        let mut s = cstr::<N>::default();
736        let (a, b) = self.to_strs();
737        s.chrs[..a.len()].copy_from_slice(a.as_bytes());
738        if b.len() > 0 {
739            s.chrs[a.len()..].copy_from_slice(b.as_bytes());
740        }
741        s.len = self.len;
742        s
743    } //resize
744
745    /// version of resize that does not allow string truncation due to length
746    pub fn reallocate<const N: usize>(&self) -> Option<cstr<N>> {
747        if self.len() < N {
748            Some(self.resize())
749        } else {
750            None
751        }
752    }
753} //impl cstr<M>
754
755impl<const N: usize> Default for cstr<N> {
756    fn default() -> Self {
757        cstr {
758            chrs: [0; N],
759            front: 0,
760            len: 0,
761        }
762    }
763} //impl default
764
765impl<const N: usize> core::fmt::Debug for cstr<N> {
766    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
767        let (a, b) = self.to_strs();
768        f.pad(a)?;
769        f.pad(b)
770    }
771} // Debug impl
772
773impl<const N: usize> core::fmt::Display for cstr<N> {
774    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
775        let (a, b) = self.to_strs();
776        //write!(f, "{}{}", a, b)
777        f.pad(a)?;
778        f.pad(b)
779    }
780}
781
782/////////// need Eq, Ord, etc.  and special iterator implementation
783impl<const N: usize> PartialEq<&str> for cstr<N> {
784    fn eq(&self, other: &&str) -> bool {
785        &self == other
786    } //eq
787}
788
789impl<const N: usize> PartialEq<&str> for &cstr<N> {
790    fn eq(&self, other: &&str) -> bool {
791        let (a, b) = self.to_strs();
792        let (alen, blen) = (a.len(), b.len());
793        alen + blen == other.len() && a == &other[..alen] && (blen == 0 || b == &other[alen..])
794    } //eq
795}
796
797impl<const N: usize> PartialEq<cstr<N>> for &str {
798    fn eq(&self, other: &cstr<N>) -> bool {
799        let (a, b) = other.to_strs();
800        let (alen, blen) = (a.len(), b.len());
801        alen + blen == self.len() && a == &self[..alen] && (blen == 0 || b == &self[alen..])
802    } //eq
803}
804
805impl<const N: usize> PartialEq<&cstr<N>> for &str {
806    fn eq(&self, other: &&cstr<N>) -> bool {
807        let (a, b) = other.to_strs();
808        let (alen, blen) = (a.len(), b.len());
809        alen + blen == self.len() && a == &self[..alen] && (blen == 0 || b == &self[alen..])
810    } //eq
811}
812
813/// character interator, returned by [cstr::chars] (available with `circular-str` option along with [cstr])
814pub struct CircCharIter<'a> {
815    first: &'a [u8],
816    second: &'a [u8],
817    index: usize,
818}
819impl<'a> Iterator for CircCharIter<'a> {
820    type Item = char;
821    fn next(&mut self) -> Option<Self::Item> {
822        if self.index < self.first.len() {
823            self.index += 1;
824            Some(self.first[self.index - 1] as char)
825        } else if self.index - self.first.len() < self.second.len() {
826            self.index += 1;
827            Some(self.second[self.index - self.first.len() - 1] as char)
828        } else {
829            None
830        }
831    } //next
832} // impl CircCharIter
833
834/// The implementation of this trait allows comparison between
835/// circular strings of different capacity.  This could affect the
836/// type inference of the [cstr::resize] function.
837impl<const N: usize, const M: usize> PartialEq<cstr<M>> for cstr<N> {
838    fn eq(&self, other: &cstr<M>) -> bool {
839        if self.len != other.len {
840            return false;
841        }
842        for i in 0..self.len {
843            if self.chrs[(self.front + i) as usize % N]
844                != other.chrs[(other.front + i) as usize % M]
845            {
846                return false;
847            }
848        } //for
849        true
850        /*
851          let mut schars = self.chars();
852          let mut ochars = other.chars();
853          loop {
854              match (schars.next(), ochars.next()) {
855                  (None, None) => {
856                      break;
857                  }
858                  (Some(x), Some(y)) if x == y => {}
859                  _ => {
860                      return false;
861                  }
862              } //match
863          } //loop
864          true
865        */
866    } //eq for Self
867} // PartialEq
868impl<const N: usize> Eq for cstr<N> {}
869
870impl<const N: usize> Ord for cstr<N> {
871    fn cmp(&self, other: &Self) -> Ordering {
872        let mut schars = self.chars();
873        let mut ochars = other.chars();
874        let mut answer = Ordering::Equal;
875        loop {
876            match (schars.next(), ochars.next()) {
877                (Some(x), Some(y)) if x.cmp(&y) == Ordering::Equal => {}
878                (Some(x), Some(y)) => {
879                    answer = x.cmp(&y);
880                    break;
881                }
882                (None, None) => {
883                    break;
884                }
885                (None, _) => {
886                    answer = Ordering::Less;
887                    break;
888                }
889                (_, None) => {
890                    answer = Ordering::Greater;
891                    break;
892                }
893            } //match
894        } //loop
895        answer
896    } //cmp
897} //Ord
898
899impl<const N: usize> PartialOrd for cstr<N> {
900    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
901        Some(self.cmp(other))
902        /*
903        let mut schars = self.chars();
904        let mut ochars = other.chars();
905        let mut answer = Ordering::Equal;
906        loop {
907          match (schars.next(), ochars.next()) {
908            (Some(x), Some(y)) if x.cmp(&y)==Ordering::Equal => {},
909            (Some(x), Some(y)) => { answer = x.cmp(&y); break; },
910            (None,None) => {break;}
911            (None,_) => { answer = Ordering::Less; break; },
912            (_,None) => { answer = Ordering::Greater; break; },
913          }//match
914        }//loop
915        Some(answer)
916        */
917    } //partial_cmp
918} // PartialOrd
919
920impl<const N: usize> PartialOrd<&str> for cstr<N> {
921    fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
922        let mut schars = self.chars();
923        let mut ochars = other.chars();
924        let mut answer = Ordering::Equal;
925        loop {
926            match (schars.next(), ochars.next()) {
927                (Some(x), Some(y)) if x.cmp(&y) == Ordering::Equal => {}
928                (Some(x), Some(y)) => {
929                    answer = x.cmp(&y);
930                    break;
931                }
932                (None, None) => {
933                    break;
934                }
935                (None, _) => {
936                    answer = Ordering::Less;
937                    break;
938                }
939                (_, None) => {
940                    answer = Ordering::Greater;
941                    break;
942                }
943            } //match
944        } //loop
945        Some(answer)
946    } //partial_cmp
947} // PartialOrd
948
949impl<const N: usize> PartialOrd<&str> for &cstr<N> {
950    fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
951        let mut schars = self.chars();
952        let mut ochars = other.chars();
953        let mut answer = Ordering::Equal;
954        loop {
955            match (schars.next(), ochars.next()) {
956                (Some(x), Some(y)) if x.cmp(&y) == Ordering::Equal => {}
957                (Some(x), Some(y)) => {
958                    answer = x.cmp(&y);
959                    break;
960                }
961                (None, None) => {
962                    break;
963                }
964                (None, _) => {
965                    answer = Ordering::Less;
966                    break;
967                }
968                (_, None) => {
969                    answer = Ordering::Greater;
970                    break;
971                }
972            } //match
973        } //loop
974        Some(answer)
975    } //partial_cmp
976} // PartialOrd
977
978/// Hashing is implemented character-by-character, starting with the
979/// last char and ending with the first
980impl<const N: usize> core::hash::Hash for cstr<N> {
981    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
982        for i in (0..self.len as usize).rev() {
983            self.nth_bytechar(i).hash(state);
984        }
985        //for c in self.chars() { c.hash(state); }
986    }
987} //hash
988
989impl<T: AsRef<str> + ?Sized, const N: usize> core::convert::From<&T> for cstr<N> {
990    fn from(s: &T) -> cstr<N> {
991        cstr::make(s.as_ref())
992    }
993}
994impl<T: AsMut<str> + ?Sized, const N: usize> core::convert::From<&mut T> for cstr<N> {
995    fn from(s: &mut T) -> cstr<N> {
996        cstr::make(s.as_mut())
997    }
998}
999
1000impl<const N: usize> core::fmt::Write for cstr<N> {
1001    fn write_str(&mut self, s: &str) -> core::fmt::Result {
1002        if s.len() + self.len() > N {
1003            return Err(core::fmt::Error::default());
1004        }
1005        self.push_str(s);
1006        Ok(())
1007    } //write_str
1008} //core::fmt::Write trait
1009
1010impl<const N: usize, TA: AsRef<str>> Add<TA> for cstr<N> {
1011    type Output = cstr<N>;
1012    fn add(self, other: TA) -> cstr<N> {
1013        let mut a2 = self;
1014        a2.push_str(other.as_ref());
1015        a2
1016    }
1017} //Add AsRef<str>
1018
1019impl<const N: usize> Add<&cstr<N>> for &str {
1020    type Output = cstr<N>;
1021    fn add(self, other: &cstr<N>) -> cstr<N> {
1022        let mut a2 = *other;
1023        a2.push_front(self);
1024        a2
1025    }
1026} //Add &str on left
1027
1028impl<const N: usize> Add<cstr<N>> for &str {
1029    type Output = cstr<N>;
1030    fn add(self, mut other: cstr<N>) -> cstr<N> {
1031        other.push_front(self);
1032        other
1033    }
1034} //Add &str on left
1035
1036impl<const N: usize> Add for &cstr<N> {
1037    type Output = cstr<N>;
1038    fn add(self, other: &cstr<N>) -> cstr<N> {
1039        let mut a2 = *self;
1040        let (l, r) = other.to_strs();
1041        a2.push_str(l);
1042        if r.len() > 0 {
1043            a2.push_str(r);
1044        }
1045        a2
1046    }
1047} //Add &str
1048
1049impl<const N: usize> Add for cstr<N> {
1050    type Output = cstr<N>;
1051    fn add(self, other: cstr<N>) -> cstr<N> {
1052        let mut a2 = self;
1053        let (l, r) = other.to_strs();
1054        a2.push_str(l);
1055        if r.len() > 0 {
1056            a2.push_str(r);
1057        }
1058        a2
1059    }
1060} //Add
1061
1062impl<const N: usize> core::str::FromStr for cstr<N> {
1063    type Err = &'static str;
1064    fn from_str(s: &str) -> Result<Self, Self::Err> {
1065        if s.len() <= N {
1066            Ok(cstr::from(s))
1067        } else {
1068            Err("capacity exceeded")
1069        }
1070    }
1071}
1072
1073/*
1074////////// fast x % n for n that are powers of 2
1075#[inline(always)]
1076fn fastmod(x:usize, n:usize) -> usize {
1077    x % n
1078//  let mask = n-1;
1079//  if n&mask==0 { x & mask } else {x % n}
1080}//fastmod
1081*/
1082
1083////// aliases
1084/// Convenient aliases for [cstr] using exact powers of 2
1085pub type cstr1k = cstr<1024>;
1086pub type cstr8 = cstr<8>;
1087pub type cstr16 = cstr<16>;
1088pub type cstr32 = cstr<32>;
1089pub type cstr64 = cstr<64>;
1090pub type cstr128 = cstr<128>;
1091pub type cstr256 = cstr<256>;
1092pub type cstr512 = cstr<512>;