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