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