fixedstr_ext/
full_fixed.rs

1//! Module for fstr type
2#![allow(unused_variables)]
3#![allow(non_snake_case)]
4#![allow(non_camel_case_types)]
5#![allow(unused_parens)]
6#![allow(unused_assignments)]
7#![allow(unused_mut)]
8#![allow(dead_code)]
9
10#[cfg(feature = "std")]
11extern crate std;
12use crate::tiny_internal::*;
13use crate::zero_terminated::*;
14use core::cmp::{min, Ordering};
15use core::ops::Add;
16use std::eprintln;
17use std::string::String;
18
19/// **This type is only available with the `std` (or `fstr`) feature.**
20/// A `fstr<N>` is a string of up to const N bytes, using a separate variable to store the length.
21/// This type is not as memory-efficient as some other types such as str4-str256.  This is also the only type of the crate that does not support `no_std`.
22#[derive(Copy, Clone, Eq)]
23pub struct fstr<const N: usize> {
24    chrs: [u8; N],
25    len: usize, // length will be <=N
26} //fstr
27impl<const N: usize> fstr<N> {
28    /// creates a new `fstr<N>` with given &str.  If the length of s exceeds
29    /// N, the extra characters are ignored and a **warning is sent to stderr**.
30    pub fn make(s: &str) -> fstr<N> {
31        let bytes = s.as_bytes(); // &[u8]
32        let mut blen = bytes.len();
33        if (blen > N) {
34            eprintln!("!Fixedstr Warning in fstr::make: length of string literal \"{}\" exceeds the capacity of type fstr<{}>; string truncated",s,N);
35            blen = N;
36        }
37        let mut chars = [0u8; N];
38        let mut i = 0;
39        let limit = min(N, blen);
40        chars[..limit].clone_from_slice(&bytes[..limit]);
41        /* //replaced re performance lint
42        for i in 0..blen
43        {
44          if i<N {chars[i] = bytes[i];} else {break;}
45        }
46        */
47        fstr {
48            chrs: chars,
49            len: blen, /* as u16 */
50        }
51    } //make
52
53    /// Version of make that does not print warning to stderr.  If the
54    /// capacity limit is exceeded, the extra characters are ignored.
55    pub fn create(s: &str) -> fstr<N> {
56        let bytes = s.as_bytes(); // &[u8]
57        let mut blen = bytes.len();
58        if (blen > N) {
59            blen = N;
60        }
61        let mut chars = [0u8; N];
62        let mut i = 0;
63        let limit = min(N, blen);
64        chars[..limit].clone_from_slice(&bytes[..limit]);
65        fstr {
66            chrs: chars,
67            len: blen,
68        }
69    } //create
70
71    /// version of make that does not truncate, if s exceeds capacity,
72    /// an Err result is returned containing s
73    pub fn try_make(s: &str) -> Result<fstr<N>, &str> {
74        if s.len() > N {
75            Err(s)
76        } else {
77            Ok(fstr::make(s))
78        }
79    }
80
81    /// const constructor, to be called from const contexts.  However, as
82    /// const constructors are restricted from using iterators, it's slightly
83    /// better to call the non-const constructors in non-const contexts.
84    /// Truncates automatically.
85    pub const fn const_create(s: &str) -> fstr<N> {
86        let mut t = fstr::<N>::new();
87        let mut len = s.len();
88        if len > N {
89            len = N;
90        } // fix max length
91        t.len = len;
92        let bytes = s.as_bytes();
93        let mut i = 0;
94        while i < len {
95            t.chrs[i] = bytes[i];
96            i += 1;
97        }
98        t
99    } //const_make
100
101    /// Create a `fstr<N>` from a slice of `&str`s. If the total length exceeds capacity N,
102    /// the remaining strings are ignored.
103    pub const fn const_create_from_str_slice(strs: &[&str]) -> fstr<N> {
104        let mut result = fstr::<N>::new();
105        let mut position = 0;
106        let mut remaining = strs;
107        while let [current, tail @ ..] = remaining {
108            let current_bytes = current.as_bytes();
109            let mut i = 0;
110            while i < current_bytes.len() && position < N {
111                result.chrs[position] = current_bytes[i];
112                position += 1;
113                i += 1;
114            }
115            if position >= N {
116                break;
117            }
118            remaining = tail;
119        }
120        result.len = position;
121        result
122    }
123
124    /// version of `const_create` that does not truncate.
125    pub const fn const_try_create(s: &str) -> Result<fstr<N>, &str> {
126        if s.len() + 1 > N {
127            Err(s)
128        } else {
129            Ok(fstr::const_create(s))
130        }
131    }
132
133    /// creates an empty string, equivalent to fstr::default() but can also be
134    /// called from a const context
135    #[inline]
136    pub const fn new() -> fstr<N> {
137        fstr {
138            chrs: [0; N],
139            len: 0,
140        }
141    } //new
142
143    /// length of the string in bytes, which will be up to the maximum size N.
144    /// This is a constant-time operation and can be called from a const
145    /// context
146    #[inline]
147    pub const fn len(&self) -> usize {
148        self.len
149    }
150
151    /// returns maximum capacity in bytes
152    pub const fn capacity(&self) -> usize {
153        N
154    }
155
156    /// converts fstr to an owned string
157    pub fn to_string(&self) -> String {
158        //self.to_str().to_owned()
159        String::from(self.to_str())
160        //self.chrs[0..self.len].iter().map(|x|{*x as char}).collect()
161    }
162
163    /// allows returns copy of u8 array underneath the fstr
164    pub const fn as_u8(&self) -> [u8; N] {
165        self.chrs
166    }
167
168    /// returns slice of the u8 array underneath
169    pub fn as_bytes(&self) -> &[u8] {
170        &self.chrs[0..self.len]
171    }
172
173    /// returns mutable slice of the u8 array underneath (use with care)
174    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
175        let n = self.len;
176        &mut self.chrs[0..n]
177    }
178
179    /// converts fstr to &str using [std::str::from_utf8_unchecked].  Since
180    /// fstr can only be built from valid utf8 sources, this function
181    /// is safe.
182    pub fn to_str(&self) -> &str {
183        unsafe { std::str::from_utf8_unchecked(&self.chrs[0..self.len]) }
184    }
185
186    /// Return a `*const u8` to internal buffer.
187    ///
188    /// Mainly used in const context.
189    pub const fn to_ptr(&'static self) -> *const u8 {
190        self.chrs.as_ptr()
191    }
192
193    /// same functionality as [fstr::to_str], but using [std::str::from_utf8]
194    /// and may technically panic.
195    pub fn as_str(&self) -> &str //{self.to_str()}
196    {
197        std::str::from_utf8(&self.chrs[0..self.len]).unwrap()
198    }
199
200    /// version of [fstr::as_str] that does not call `unwrap`
201    pub fn as_str_safe(&self) -> Result<&str, core::str::Utf8Error> {
202        core::str::from_utf8(&self.chrs[0..self.len])
203    }
204
205    /// changes a character at character position i to c.  This function
206    /// requires that c is in the same character class (ascii or unicode)
207    /// as the char being replaced.  It never shuffles the bytes underneath.
208    /// The function returns true if the change was successful.
209    pub fn set(&mut self, i: usize, c: char) -> bool {
210        let ref mut cbuf = [0u8; 4]; // characters require at most 4 bytes
211        c.encode_utf8(cbuf);
212        let clen = c.len_utf8();
213        if let Some((bi, rc)) = self.to_str().char_indices().nth(i) {
214            if clen == rc.len_utf8() {
215                self.chrs[bi..bi + clen].clone_from_slice(&cbuf[..clen]);
216                //for k in 0..clen {self.chrs[bi+k] = cbuf[k];}
217                return true;
218            }
219        }
220        return false;
221    }
222    /// adds chars to end of current string up to maximum size N of `fstr<N>`,
223    /// returns the portion of the push string that was NOT pushed due to
224    /// capacity, so
225    /// if "" is returned then all characters were pushed successfully.
226    pub fn push<'t>(&mut self, s: &'t str) -> &'t str {
227        self.push_str(s)
228        /*
229        if s.len() < 1 {
230            return s;
231        }
232        let mut buf = [0u8; 4];
233        let mut i = self.len();
234        let mut sci = 0; // indexes characters in s
235        for c in s.chars() {
236            let clen = c.len_utf8();
237            c.encode_utf8(&mut buf);
238            if i+clen <= N {
239                self.chrs[i..i + clen].clone_from_slice(&buf[..clen]);
240                /*
241                for k in 0..clen
242                {
243                  self.chrs[i+k] = buf[k];
244                }
245                */
246                i += clen;
247            } else {
248                self.len = i;
249                return &s[sci..];
250            }
251            sci += clen;
252        }
253        self.len = i;
254        &s[sci..]
255        */
256    } //push
257
258    /// alias for [fstr::push]
259    pub fn push_str<'t>(&mut self, src: &'t str) -> &'t str {
260        let srclen = src.len();
261        let slen = self.len();
262        let bytes = &src.as_bytes();
263        let length = core::cmp::min(slen + srclen, N);
264        let remain = if N >= (slen + srclen) {
265            0
266        } else {
267            (srclen + slen) - N
268        };
269        let mut i = 0;
270        while i < srclen && i + slen < N {
271            self.chrs[slen + i] = bytes[i];
272            i += 1;
273        } //while
274        self.len += i;
275        &src[srclen - remain..]
276    }
277
278    /// pushes a single character to the end of the string, returning
279    /// true on success.
280    pub fn push_char(&mut self, c: char) -> bool {
281        let clen = c.len_utf8();
282        if self.len + clen > N {
283            return false;
284        }
285        let mut buf = [0u8; 4]; // char buffer
286        let bstr = c.encode_utf8(&mut buf);
287        self.push(bstr);
288        true
289    } // push_char
290
291    /// remove and return last character in string, if it exists
292    pub fn pop_char(&mut self) -> Option<char> {
293        if self.len() == 0 {
294            return None;
295        }
296        let (ci, lastchar) = self.char_indices().last().unwrap();
297        self.len = ci;
298        Some(lastchar)
299    } //pop
300
301    /// returns the number of characters in the string regardless of
302    /// character class
303    pub fn charlen(&self) -> usize {
304        self.to_str().chars().count()
305    }
306
307    /// returns the nth char of the fstr
308    pub fn nth(&self, n: usize) -> Option<char> {
309        self.to_str().chars().nth(n)
310    }
311
312    /// returns the nth byte of the string as a char.  This
313    /// function should only be called, for example, on ascii strings.  It
314    /// is designed to be quicker than [fstr::nth], and does not check array bounds or
315    /// check n against the length of the string. Nor does it check
316    /// if the value returned is a valid character.
317    pub const fn nth_bytechar(&self, n: usize) -> char {
318        self.chrs[n] as char
319    }
320    /// alias for [Self::nth_bytechar] (for backwards compatibility)
321    pub const fn nth_ascii(&self, n: usize) -> char {
322        self.chrs[n] as char
323    }
324
325    /// shortens the fstr in-place (mutates).  Note that n indicates
326    /// a *character* position to truncate up to, not the byte position.
327    //  If n is greater than the
328    /// current length of the string in chars, this operation will have no effect.
329    pub fn truncate(&mut self, n: usize) {
330        if let Some((bi, c)) = self.to_str().char_indices().nth(n) {
331            //self.chrs[bi] = 0;
332            self.len = bi;
333        }
334        //if n<self.len {self.len = n;}
335    }
336
337    /// truncates string up to *byte* position n.  **Panics** if n is
338    /// not on a character boundary, similar to [String::truncate]
339    pub fn truncate_bytes(&mut self, n: usize) {
340        if (n < self.len) {
341            assert!(self.is_char_boundary(n));
342            self.len = n
343        }
344    }
345
346    /// Trims **in-place** trailing ascii whitespaces.  This function
347    /// regards all bytes as single chars.  The operation panics if
348    /// the resulting string does not end on a character boundary.
349    pub fn right_ascii_trim(&mut self) {
350        let mut n = self.len;
351        while n > 0 && (self.chrs[n - 1] as char).is_ascii_whitespace() {
352            //self.chrs[n-1] = 0;
353            n -= 1;
354        }
355        assert!(self.is_char_boundary(n));
356        self.len = n;
357    } //right_trim
358
359    /// resets string to empty string
360    pub fn clear(&mut self) {
361        self.len = 0;
362    }
363
364    /// in-place modification of ascii characters to lower-case, panics if
365    /// the string is not ascii.
366    pub fn make_ascii_lowercase(&mut self) {
367        assert!(self.is_ascii());
368        for b in &mut self.chrs[..self.len] {
369            if *b >= 65 && *b <= 90 {
370                *b |= 32;
371            }
372        }
373    } //make_ascii_lowercase
374
375    /// in-place modification of ascii characters to upper-case, panics if
376    /// the string is not ascii.
377    pub fn make_ascii_uppercase(&mut self) {
378        assert!(self.is_ascii());
379        for b in &mut self.chrs[..self.len] {
380            if *b >= 97 && *b <= 122 {
381                *b -= 32;
382            }
383        }
384    }
385
386    /// Constructs a clone of this fstr but with only upper-case ascii
387    /// characters.  This contrasts with [str::to_ascii_uppercase],
388    /// which creates an owned String.
389    pub fn to_ascii_upper(&self) -> Self {
390        let mut cp = self.clone();
391        cp.make_ascii_uppercase();
392        cp
393    }
394
395    /// Constructs a clone of this fstr but with only lower-case ascii
396    /// characters.  This contrasts with [str::to_ascii_lowercase],
397    /// which creates an owned String.
398    pub fn to_ascii_lower(&self) -> Self {
399        let mut cp = *self;
400        cp.make_ascii_lowercase();
401        cp
402    }
403
404    /// Tests for ascii case-insensitive equality with another string.
405    /// This function does not check if either string is ascii.
406    pub fn case_insensitive_eq<TA>(&self, other: TA) -> bool
407    where
408        TA: AsRef<str>,
409    {
410        if self.len() != other.as_ref().len() {
411            return false;
412        }
413        let obytes = other.as_ref().as_bytes();
414        for i in 0..self.len() {
415            let mut c = self.chrs[i];
416            if (c > 64 && c < 91) {
417                c = c | 32;
418            } // make lowercase
419            let mut d = obytes[i];
420            if (d > 64 && d < 91) {
421                d = d | 32;
422            } // make lowercase
423            if c != d {
424                return false;
425            }
426        } //for
427        true
428    } //case_insensitive_eq
429
430    /// Decodes a UTF-16 encodeded slice. If a decoding error is encountered
431    /// or capacity exceeded, an `Err(s)` is returned where s is the
432    /// the encoded string up to the point of the error.
433    pub fn from_utf16(v: &[u16]) -> Result<Self, Self> {
434        let mut s = Self::new();
435        for c in char::decode_utf16(v.iter().cloned()) {
436            if let Ok(c1) = c {
437                if !s.push_char(c1) {
438                    return Err(s);
439                }
440            } else {
441                return Err(s);
442            }
443        }
444        Ok(s)
445    } //from_utf16
446} //impl fstr<N>
447
448impl<const N: usize> std::ops::Deref for fstr<N> {
449    type Target = str;
450    fn deref(&self) -> &Self::Target {
451        self.to_str()
452    }
453}
454
455impl<T: AsRef<str> + ?Sized, const N: usize> std::convert::From<&T> for fstr<N> {
456    fn from(s: &T) -> fstr<N> {
457        fstr::make(s.as_ref())
458    }
459}
460impl<T: AsMut<str> + ?Sized, const N: usize> std::convert::From<&mut T> for fstr<N> {
461    fn from(s: &mut T) -> fstr<N> {
462        fstr::make(s.as_mut())
463    }
464}
465
466/*
467//generic, but "conflicts with crate 'core'
468impl<const N: usize, TA:AsRef<str>> std::convert::From<TA> for fstr<N> {
469    fn from(s: TA) -> fstr<N> {
470        fstr::<N>::make(s.as_ref())
471    }
472}
473*/
474
475impl<const N: usize> std::convert::From<String> for fstr<N> {
476    fn from(s: String) -> fstr<N> {
477        fstr::<N>::make(&s[..])
478    }
479}
480
481impl<const N: usize, const M: usize> std::convert::From<zstr<M>> for fstr<N> {
482    fn from(s: zstr<M>) -> fstr<N> {
483        fstr::<N>::make(&s.to_str())
484    }
485}
486
487impl<const N: usize, const M: usize> std::convert::From<tstr<M>> for fstr<N> {
488    fn from(s: tstr<M>) -> fstr<N> {
489        fstr::<N>::make(&s.to_str())
490    }
491}
492
493impl<const N: usize> std::cmp::PartialOrd for fstr<N> {
494    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
495        Some(self.cmp(other))
496    }
497}
498
499impl<const N: usize> std::cmp::Ord for fstr<N> {
500    fn cmp(&self, other: &Self) -> Ordering {
501        self.chrs[0..self.len].cmp(&other.chrs[0..other.len])
502    }
503}
504
505impl<const M: usize> fstr<M> {
506    /// converts an fstr\<M\> to an fstr\<N\>. If the length of the string being
507    /// converted is greater than N, the extra characters are ignored.
508    /// This operation produces a copy (non-destructive).
509    /// Example:
510    ///```ignore
511    ///  let s1:fstr<8> = fstr::from("abcdefg");
512    ///  let s2:fstr<16> = s1.resize();
513    ///```
514    pub fn resize<const N: usize>(&self) -> fstr<N> {
515        //if (self.len()>N) {eprintln!("!Fixedstr Warning in fstr::resize: string \"{}\" truncated while resizing to fstr<{}>",self,N);}
516        let length = if (self.len < N) { self.len } else { N };
517        let mut chars = [0u8; N];
518        chars[..length].clone_from_slice(&self.chrs[..length]);
519        //for i in 0..length {chars[i] = self.chrs[i];}
520        fstr {
521            chrs: chars,
522            len: length,
523        }
524    } //resize
525
526    /// version of resize that does not allow string truncation due to length
527    pub fn reallocate<const N: usize>(&self) -> Option<fstr<N>> {
528        if self.len() <= N {
529            Some(self.resize())
530        } else {
531            None
532        }
533    }
534} //impl fstr<M>
535
536impl<const N: usize> std::convert::AsRef<str> for fstr<N> {
537    fn as_ref(&self) -> &str {
538        self.to_str()
539    }
540}
541impl<const N: usize> std::convert::AsMut<str> for fstr<N> {
542    fn as_mut(&mut self) -> &mut str {
543        unsafe { std::str::from_utf8_unchecked_mut(&mut self.chrs[0..self.len]) }
544    }
545}
546
547impl<const N: usize> std::fmt::Display for fstr<N> {
548    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
549        //write!(f, "{}", self.to_str())
550        f.pad(self.to_str())
551    }
552}
553
554impl<const N: usize> PartialEq<&str> for fstr<N> {
555    fn eq(&self, other: &&str) -> bool {
556        &self.to_str() == other // see below
557    } //eq
558}
559
560impl<const N: usize> PartialEq<&str> for &fstr<N> {
561    fn eq(&self, other: &&str) -> bool {
562        &self.to_str() == other
563    } //eq
564}
565impl<'t, const N: usize> PartialEq<fstr<N>> for &'t str {
566    fn eq(&self, other: &fstr<N>) -> bool {
567        &other.to_str() == self
568    }
569}
570impl<'t, const N: usize> PartialEq<&fstr<N>> for &'t str {
571    fn eq(&self, other: &&fstr<N>) -> bool {
572        &other.to_str() == self
573    }
574}
575
576/// defaults to empty string
577impl<const N: usize> Default for fstr<N> {
578    fn default() -> Self {
579        fstr::<N>::make("")
580    }
581}
582
583impl<const N: usize> std::fmt::Debug for fstr<N> {
584    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
585        f.pad(&self.to_str())
586    }
587} // Debug impl
588
589impl<const N: usize> fstr<N> {
590    /// returns a copy of the portion of the string, string could be truncated
591    /// if indices are out of range. Similar to slice [start..end]
592    pub fn substr(&self, start: usize, end: usize) -> fstr<N> {
593        let mut chars = [0u8; N];
594        let mut inds = self.char_indices();
595        let len = self.len();
596        if start >= len || end <= start {
597            return fstr {
598                chrs: chars,
599                len: 0,
600            };
601        }
602        let (si, _) = inds.nth(start).unwrap();
603        let last = if (end >= len) {
604            len
605        } else {
606            match inds.nth(end - start - 1) {
607                Some((ei, _)) => ei,
608                None => len,
609            } //match
610        }; //let last =...
611
612        chars[0..last - si].clone_from_slice(&self.chrs[si..last]);
613        /*
614        for i in si..last
615        {
616          chars[i-si] = self.chrs[i];
617        }
618        */
619        fstr {
620            chrs: chars,
621            len: end - start,
622        }
623    } //substr
624}
625
626////////////// core::fmt::Write trait
627/// Usage:
628/// ```
629///   use fixedstr_ext::*;
630///   use std::fmt::Write;
631///   let mut s = fstr::<32>::new();
632///   let result = write!(&mut s,"hello {}, {}, {}",1,2,3);
633///   /* or */
634///   let s2 = str_format!(fstr<24>,"hello {}, {}, {}",1,2,3);
635///   let s3 = try_format!(fstr::<4>,"hello {}, {}, {}",1,2,3); // returns None
636/// ```
637impl<const N: usize> core::fmt::Write for fstr<N> {
638    fn write_str(&mut self, s: &str) -> std::fmt::Result //Result<(),std::fmt::Error>
639    {
640        let rest = self.push(s);
641        if rest.len() > 0 {
642            return Err(core::fmt::Error::default());
643        }
644        Ok(())
645    } //write_str
646} //core::fmt::Write trait
647
648impl<const N: usize, TA: AsRef<str>> Add<TA> for fstr<N> {
649    type Output = fstr<N>;
650    fn add(self, other: TA) -> fstr<N> {
651        let mut a2 = self;
652        a2.push(other.as_ref());
653        a2
654    }
655} //Add &str
656
657impl<const N: usize> Add<&fstr<N>> for &str {
658    type Output = fstr<N>;
659    fn add(self, other: &fstr<N>) -> fstr<N> {
660        let mut a2 = fstr::from(self);
661        a2.push(other);
662        a2
663    }
664} //Add &str on left
665
666impl<const N: usize> Add<fstr<N>> for &str {
667    type Output = fstr<N>;
668    fn add(self, other: fstr<N>) -> fstr<N> {
669        let mut a2 = fstr::from(self);
670        a2.push(&other);
671        a2
672    }
673} //Add &str on left
674
675impl<const N: usize> core::hash::Hash for fstr<N> {
676    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
677        self.as_ref().hash(state);
678    }
679} //hash
680  /*  can't adopt because it affects type inference for .resize()
681  impl<const N: usize, const M:usize> PartialEq<fstr<M>> for fstr<N> {
682      fn eq(&self, other: &fstr<M>) -> bool {
683         self.as_ref() == other.as_ref()
684      }
685  }
686  */
687impl<const N: usize> PartialEq for fstr<N> {
688    fn eq(&self, other: &Self) -> bool {
689        self.as_ref() == other.as_ref()
690    }
691}
692
693impl<const N: usize> core::str::FromStr for fstr<N> {
694    type Err = &'static str;
695    fn from_str(s: &str) -> Result<Self, Self::Err> {
696        if s.len() <= N {
697            Ok(fstr::from(s))
698        } else {
699            Err("capacity exceeded")
700        }
701    }
702}
703
704#[cfg(test)]
705mod tests {
706    use super::fstr;
707
708    #[test]
709    fn test_create_fstr_from_str_slice_success() {
710        let slice = &["12", "34", "567"];
711        let _: fstr<8> = fstr::const_create_from_str_slice(slice);
712        let slice = &["12", "34", "5678"];
713        let _: fstr<8> = fstr::const_create_from_str_slice(slice);
714        let slice = &["12", "34", "5678", "90000000"];
715        let _: fstr<8> = fstr::const_create_from_str_slice(slice);
716    }
717}