fixedstr/
flexible_string.rs

1//! This module implements **[Flexstr]**, which uses an internal enum
2//! to hold either a fixed string of up to a maximum length, or an owned [String].
3
4#![allow(unused_variables)]
5#![allow(non_snake_case)]
6#![allow(non_camel_case_types)]
7#![allow(unused_parens)]
8#![allow(unused_assignments)]
9#![allow(unused_mut)]
10#![allow(unused_imports)]
11#![allow(dead_code)]
12extern crate alloc;
13use crate::tstr;
14use crate::zstr;
15
16#[cfg(feature = "std")]
17use crate::fstr;
18
19use crate::shared_structs::Strunion;
20use crate::shared_structs::Strunion::*;
21use crate::{str12, str128, str16, str192, str24, str256, str32, str4, str48, str64, str8, str96};
22use alloc::string::String;
23use alloc::vec::Vec;
24use core::cmp::{min, Ordering};
25use core::ops::Add;
26
27/// **This type is only available with the `flex-str` option.**
28/// A `Flexstr<N>` is represented internally as a `tstr<N>` if the length of
29/// the string is less than N bytes, and by an owned String otherwise.
30/// The structure satisfies the following axiom:
31/// >   *For N <= 256, a `Flexstr<N>` is represented internally by an
32///     owned String if and only if the length of the string is greater than
33///     or equal to N*.
34///
35/// For example, a `Flexstr<16>` will hold a string of up to 15 bytes
36/// in an u8-array of size 16. The first byte of the array holds the length of
37/// the string.  If subsequent operations such as [Flexstr::push_str]
38/// extends the string past 15 bytes, the representation will switch to an owned
39/// String.  Conversely, an operation such as [Flexstr::truncate]
40/// may switch the representation back to a fixed string.
41/// The default N is 32.  **The largest N for which the axiom holds
42/// is 256.**  For all N>256, the internal representation is always an owned
43/// string.
44///
45/// Example:
46/// ```ignore
47///  let mut s:Flexstr<8> = Flexstr::from("abcdef");
48///  assert!(s.is_fixed());
49///  s.push_str("ghijk");
50///  assert!(s.is_owned());
51///  s.truncate(7);
52///  assert!(s.is_fixed());
53/// ```
54///
55/// The intended use of this datatype is for
56/// situations when the lengths of strings are *usually* less than N, with
57/// only occasional exceptions that require a different representation.
58/// However, unlike the other string types in this crate, a Flexstr cannot be
59/// copied and is thus subject to **move semantics**.  The serde serialization
60/// option is also supported (`features serde`).
61///
62/// In addition, this type impls the `Add` trait for string concatenation:
63///
64/// ```ignore
65///  let a = flexstr8::from("abcd");
66///  let b = &a + "efg";
67///  assert_eq!(&b,"abcdefg");
68/// ```
69///
70#[derive(Clone, Eq)]
71pub struct Flexstr<const N: usize = 32> {
72    inner: Strunion<N>,
73}
74impl<const N: usize> Flexstr<N> {
75    /// Creates a new `Flexstr<N>` with given &str.
76    pub fn make(s: &str) -> Self {
77        if s.len() < N && N <= 256 {
78            Flexstr {
79                inner: fixed(tstr::<N>::from(s)),
80            }
81        } else {
82            Flexstr {
83                inner: owned(String::from(s)),
84            }
85        }
86    } //make
87
88    /// Creates a `Flexstr<N>` by consuming a given string.  However, if the
89    /// string has length less than N, then a fixed representation will be used.
90    pub fn from_string(s: String) -> Self {
91        if s.len() < N && N <= 256 {
92            Flexstr {
93                inner: fixed(tstr::<N>::from(&s[..])),
94            }
95        } else {
96            Flexstr { inner: owned(s) }
97        }
98    }
99
100    /// creates a `Flexstr<N>` from a given `tstr<N>`
101    pub fn from_tstr(s: tstr<N>) -> Self {
102        Flexstr { inner: fixed(s) }
103    }
104
105    #[cfg(feature = "serde")]
106    /// this function is only added for uniformity in serde implementation
107    pub fn try_make(s: &str) -> Result<Flexstr<N>, &str> {
108        Ok(Flexstr::make(s))
109    }
110
111    /// length of the string in bytes. This is a constant-time operation.
112    #[inline]
113    pub fn len(&self) -> usize {
114        match &self.inner {
115            fixed(s) => s.len(),
116            owned(s) => s.len(),
117        } //match
118    } //len
119
120    /// creates an empty string, equivalent to [Flexstr::default]
121    #[inline]
122    pub fn new() -> Self {
123        Self::default()
124    }
125
126    /// length in number of characters as opposed to bytes: this is
127    /// not necessarily a constant time operation.
128    pub fn charlen(&self) -> usize {
129        match &self.inner {
130            fixed(s) => s.charlen(),
131            owned(s) => {
132                s.chars().count()
133                //let v: Vec<_> = s.chars().collect();
134                //v.len()
135            }
136        } //match
137    } //charlen
138
139    /// converts fstr to &str, possibly using using [core::str::from_utf8_unchecked].  Since
140    /// Flexstr can only be built from valid utf8 sources, this function
141    /// is safe.
142    pub fn to_str(&self) -> &str {
143        match &self.inner {
144            fixed(s) => s.to_str(),
145            owned(s) => &s[..],
146        } //match
147    } //to_str
148
149    /// same functionality as [Flexstr::to_str], but only uses
150    ///[core::str::from_utf8] and may technically panic.
151    pub fn as_str(&self) -> &str //{self.to_str()}
152    {
153        match &self.inner {
154            fixed(s) => s.as_str(),
155            owned(s) => &s[..],
156        } //match
157    }
158
159    /// version of [Flexstr::as_str] that does not call `unwrap`
160    pub fn as_str_safe(&self) -> Result<&str,core::str::Utf8Error>
161    {
162        match &self.inner {
163            fixed(s) => s.as_str_safe(),
164            owned(s) => Ok(&s[..]),
165        } //match
166    }
167    
168
169    /// retrieves a copy of the underlying fixed string, if it is a fixed string.
170    /// Note that since the `tstr` type is not exported, this function should
171    /// be used in conjunction with one of the public aliases [str4]-[str256].
172    /// For example,
173    /// ```ignore
174    ///   let s = Flexstr::<8>::from("abcd");
175    ///   let t:str8 = s.get_str().unwrap();
176    /// ```
177    pub fn get_str(&self) -> Option<tstr<N>> {
178        if let fixed(s) = &self.inner {
179            Some(*s)
180        } else {
181            None
182        }
183    } //get_str
184
185    /// if the underlying representation of the string is an owned string,
186    /// return the owned string, leaving an empty string in its place.
187    pub fn take_string(&mut self) -> Option<String> {
188        if let owned(s) = &mut self.inner {
189            let mut temp = fixed(tstr::new());
190            core::mem::swap(&mut self.inner, &mut temp);
191            if let owned(t) = temp {
192                Some(t)
193            } else {
194                None
195            }
196        } else {
197            None
198        }
199    } //take_owned
200
201    /// this function consumes the Flexstr and returns an owned string
202    pub fn to_string(self) -> String {
203        match self.inner {
204            fixed(s) => s.to_string(),
205            owned(s) => s,
206        } //match
207    } //to_string
208
209    /// returns the nth char of the string, if it exists
210    pub fn nth(&self, n: usize) -> Option<char> {
211        self.to_str().chars().nth(n)
212    }
213
214    /// returns the nth byte of the string as a char.  This function
215    /// is designed to be quicker than [Flexstr::nth] and does not check
216    /// for bounds.
217    pub fn nth_bytechar(&self, n: usize) -> char {
218        match &self.inner {
219            fixed(s) => s.nth_ascii(n),
220            owned(s) => s.as_bytes()[n] as char,
221        }
222    } //nth_bytechar
223
224    /// alias for [Self::nth_bytechar] (for backwards compatibility)
225    pub fn nth_ascii(&self, n: usize) -> char {
226        self.nth_bytechar(n)
227    }
228
229    /// returns a u8-slice that represents the underlying string. The first
230    /// byte of the slice is **not** the length of the string regarless of
231    /// the internal representation.
232    pub fn as_bytes(&self) -> &[u8] {
233        match &self.inner {
234            fixed(f) => f.as_bytes(),
235            owned(s) => s.as_bytes(),
236        } //match
237    }
238
239   /// returns mutable u8-slice of string underneath.  Function requires
240   /// call to [String::as_mut_str] and is therefore marked unsafe.
241    pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
242        match &mut self.inner {
243            fixed(f) => f.as_bytes_mut(),
244            owned(s) => s.as_mut_str().as_bytes_mut(),
245        } //match
246    }
247
248    /// changes a character at character position i to c.  This function
249    /// requires that c is in the same character class (ascii or unicode)
250    /// as the char being replaced.  It never shuffles the bytes underneath.
251    /// The function returns true if the change was successful.
252    pub fn set(&mut self, i: usize, c: char) -> bool {
253        match &mut self.inner {
254            fixed(s) => s.set(i, c),
255            owned(s) => unsafe {
256                let ref mut cbuf = [0u8; 4];
257                c.encode_utf8(cbuf);
258                let clen = c.len_utf8();
259                if let Some((bi, rc)) = s.char_indices().nth(i) {
260                    if clen == rc.len_utf8() {
261                        s.as_bytes_mut()[bi..bi + clen].copy_from_slice(&cbuf[..clen]);
262                        //self.chrs[bi + 1..bi + clen + 1].copy_from_slice(&cbuf[..clen]);
263                        //for k in 0..clen {self.chrs[bi+k+1] = cbuf[k];}
264                        return true;
265                    }
266                }
267                return false;
268            },
269        } //match
270    } //set
271
272    /// returns whether the internal representation is a fixed string (tstr)
273    pub fn is_fixed(&self) -> bool {
274        match &self.inner {
275            fixed(_) => true,
276            owned(_) => false,
277        }
278    } //is_fixed
279
280    /// returns whether the internal representation is an owned String
281    pub fn is_owned(&self) -> bool {
282        !self.is_fixed()
283    }
284
285    /// applies the destructive closure only if the internal representation
286    /// is a fixed string
287    pub fn if_fixed<F>(&mut self, f: F)
288    where
289        F: FnOnce(&mut tstr<N>),
290    {
291        if let fixed(s) = &mut self.inner {
292            f(s);
293        }
294    }
295
296    /// applies the destructive closure only if the internal representation
297    /// is a fixed string
298    pub fn if_owned<F>(&mut self, f: F)
299    where
300        F: FnOnce(&mut str),
301    {
302        if let owned(s) = &mut self.inner {
303            f(s);
304        }
305    }
306
307    /// applies closure f if the internal representation is a fixed string,
308    /// or closure g if the internal representation is an owned string.
309    pub fn map_or<F, G, U>(&self, f: F, g: G) -> U
310    where
311        F: FnOnce(&tstr<N>) -> U,
312        G: FnOnce(&str) -> U,
313    {
314        match &self.inner {
315            fixed(s) => f(s),
316            owned(s) => g(&s[..]),
317        } //match
318    } //map
319
320    /// version of [Flexstr::map_or] accepting FnMut closures
321    pub fn map_or_mut<F, G, U>(&mut self, f: &mut F, g: &mut G) -> U
322    where
323        F: FnMut(&mut tstr<N>) -> U,
324        G: FnMut(&mut str) -> U,
325    {
326        match &mut self.inner {
327            fixed(s) => f(s),
328            owned(s) => g(&mut s[..]),
329        } //match
330    } //map
331
332    /// This function will append the Flexstr with the given slice,
333    /// switching to the owned-String representation if necessary.  The function
334    /// returns true if the resulting string uses a `tstr<N>` type, and
335    /// false if the representation is an owned string.
336    pub fn push_str(&mut self, s: &str) -> bool {
337        match &mut self.inner {
338            fixed(fs) if fs.len() + s.len() < N => {
339                fs.push(s);
340                true
341            }
342            fixed(fs) => {
343                let fss = fs.to_string() + s;
344                self.inner = owned(fss);
345                false
346            }
347            owned(ns) => {
348                ns.push_str(s);
349                false
350            }
351        } //match
352    } //push_str
353
354    /// appends string with a single character, switching to the String
355    /// representation if necessary.  Returns true if resulting string
356    /// remains fixed.
357    pub fn push(&mut self, c: char) -> bool {
358        let clen = c.len_utf8();
359        match &mut self.inner {
360            owned(s) => {
361                s.push(c);
362                false
363            }
364            fixed(s) if s.len() + clen >= N => {
365                let mut fss = s.to_string();
366                fss.push(c);
367                self.inner = owned(fss);
368                false
369            }
370            fixed(s) => {
371                let mut buf = [0u8; 4];
372                let bstr = c.encode_utf8(&mut buf);
373                s.push(bstr);
374                true
375            }
376        } //match
377    } //push
378
379    /// alias for push
380    #[inline]
381    pub fn push_char(&mut self, c: char) -> bool {
382        self.push(c)
383    }
384
385    /// remove and return last character in string, if it exists
386    pub fn pop(&mut self) -> Option<char> {
387        if self.len() == 0 {
388            return None;
389        }
390        match &mut self.inner {
391            fixed(s) => s.pop_char(),
392            owned(s) if s.len() > N => s.pop(),
393            owned(s) => {
394                // change representation
395                let lastchar = s.pop();
396                self.inner = fixed(tstr::from(&s));
397                lastchar
398            }
399        } //match
400    } //pop
401
402    /// alias for [Self::pop]
403    pub fn pop_char(&mut self) -> Option<char> {
404        self.pop()
405    }
406
407    /// this function truncates a string at the indicated byte position,
408    /// returning true if the truncated string is fixed, and false if owned.
409    /// The operation has no effect if n is larger than the length of the
410    /// string.  The operation will **panic** if n is not on a character
411    /// boundary, similar to [String::truncate].
412    pub fn truncate(&mut self, n: usize) -> bool {
413        match &mut self.inner {
414            fixed(fs) if n < fs.len() => {
415                fs.truncate_bytes(n);
416                true
417            }
418            fixed(_) => true,
419            owned(s) if n < N => {
420                assert!(s.is_char_boundary(n));
421                self.inner = fixed(tstr::<N>::from(&s[..n]));
422                true
423            }
424            owned(s) => {
425                if n < s.len() {
426                    s.truncate(n);
427                }
428                false
429            }
430        } //match
431    } //truncate
432
433    /// resets string to empty
434    pub fn clear(&mut self) {
435        match &mut self.inner {
436            fixed(s) => {
437                s.clear();
438            }
439            owned(s) => {
440                self.inner = fixed(tstr::default());
441            }
442        }
443    } //clear
444
445    /// returns string corresponding to slice indices as a copy or clone.
446    pub fn substr(&self, start: usize, end: usize) -> Flexstr<N> {
447        match &self.inner {
448            fixed(s) => Flexstr {
449                inner: fixed(s.substr(start, end)),
450            },
451            owned(s) => Self::from(&s[start..end]),
452        }
453    } //substr
454
455    /// Splits the string into a `tstr<N>` portion and a String portion.
456    /// The structure inherits the fixed part and the String returned will
457    /// contain the extra bytes that does not fit.  Example:
458    ///
459    /// ```
460    ///  # use fixedstr::*;
461    ///   let mut fs:Flexstr<4> = Flexstr::from("abcdefg");
462    ///   let extras = fs.split_off();
463    ///   assert!( &fs=="abc" && &extras=="defg" && fs.is_fixed());
464    /// ```
465    pub fn split_off(&mut self) -> String {
466        match &mut self.inner {
467            fixed(s) => String::default(),
468            owned(s) => {
469                let answer = String::from(&s[N - 1..]);
470                self.inner = fixed(tstr::<N>::from(&s[..N - 1]));
471                answer
472            }
473        } //match
474    } //split_off
475
476    /// in-place modification of ascii string to lower-case
477    pub fn make_ascii_lowercase(&mut self) {
478        match &mut self.inner {
479            fixed(s) => {
480                s.make_ascii_lowercase();
481            }
482            owned(s) => {
483                s.as_mut_str().make_ascii_lowercase();
484            }
485        } //match
486    } //make_ascii_lowercase
487
488    /// in-place modification of ascii string to upper-case
489    pub fn make_ascii_uppercase(&mut self) {
490        match &mut self.inner {
491            fixed(s) => {
492                s.make_ascii_uppercase();
493            }
494            owned(s) => {
495                s.as_mut_str().make_ascii_uppercase();
496            }
497        } //match
498    }
499
500    /// Tests for ascii case-insensitive equality with another string.
501    /// This function does not test if either string is ascii.
502    pub fn case_insensitive_eq<TA>(&self, other: TA) -> bool
503    where
504        TA: AsRef<str>,
505    {
506        if self.len() != other.as_ref().len() {
507            return false;
508        }
509        let obytes = other.as_ref().as_bytes();
510        let sbytes = self.as_bytes();
511        for i in 0..self.len() {
512            let mut c = sbytes[i];
513            if (c > 64 && c < 91) {
514                c = c | 32;
515            } // make lowercase
516            let mut d = obytes[i];
517            if (d > 64 && d < 91) {
518                d = d | 32;
519            } // make lowercase
520            if c != d {
521                return false;
522            }
523        } //for
524        true
525    } //case_insensitive_eq
526
527    /// Decodes a UTF-16 encodeded slice. If a decoding error is encountered
528    /// or capacity exceeded, an `Err(s)` is returned where s is the
529    /// the encoded string up to the point of the error.
530    pub fn from_utf16(v: &[u16]) -> Result<Self, Self> {
531        let mut s = Self::new();
532        for c in char::decode_utf16(v.iter().cloned()) {
533            if let Ok(c1) = c {
534                if !s.push_char(c1) {
535                    return Err(s);
536                }
537            } else {
538                return Err(s);
539            }
540        }
541        Ok(s)
542    } //from_utf16
543} //impl<N>
544
545impl<const N: usize> Default for Flexstr<N> {
546    fn default() -> Self {
547        Flexstr {
548            inner: fixed(tstr::<N>::default()),
549        }
550    }
551}
552
553/*
554impl<const N:usize> core::hash::Hash for Flexstr<N>
555{
556  fn hash<H:core::hash::Hasher>(&self, state:&mut H) {
557    self.as_str().hash(state)
558  }
559}//hash
560*/
561
562impl<const N: usize> core::ops::Deref for Flexstr<N> {
563    type Target = str;
564    fn deref(&self) -> &Self::Target {
565        self.to_str()
566    }
567}
568
569impl<T: AsRef<str> + ?Sized, const N: usize> core::convert::From<&T> for Flexstr<N> {
570    fn from(s: &T) -> Self {
571        Self::make(s.as_ref())
572    }
573}
574
575impl<T: AsMut<str> + ?Sized, const N: usize> core::convert::From<&mut T> for Flexstr<N> {
576    fn from(s: &mut T) -> Self {
577        Self::make(s.as_mut())
578    }
579}
580
581impl<const N: usize> core::cmp::PartialOrd for Flexstr<N> {
582    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
583        //Some(self.chrs[0..self.len].cmp(other.chrs[0..other.len]))
584        Some(self.cmp(other))
585    }
586}
587
588impl<const N: usize> core::cmp::Ord for Flexstr<N> {
589    fn cmp(&self, other: &Self) -> Ordering {
590        self.to_str().cmp(other.to_str())
591    }
592}
593
594impl<const N: usize> core::convert::AsRef<str> for Flexstr<N> {
595    fn as_ref(&self) -> &str {
596        self.to_str()
597    }
598}
599impl<const N: usize> core::convert::AsMut<str> for Flexstr<N> {
600    fn as_mut(&mut self) -> &mut str {
601        match &mut self.inner {
602            fixed(f) => f.as_mut(),
603            owned(s) => s.as_mut(),
604        } //match
605    }
606}
607
608impl<const N: usize> PartialEq<&str> for Flexstr<N> {
609    fn eq(&self, other: &&str) -> bool {
610        &self.to_str() == other // see below
611    } //eq
612}
613
614impl<const N: usize> PartialEq<&str> for &Flexstr<N> {
615    fn eq(&self, other: &&str) -> bool {
616        &self.to_str() == other
617    } //eq
618}
619impl<'t, const N: usize> PartialEq<Flexstr<N>> for &'t str {
620    fn eq(&self, other: &Flexstr<N>) -> bool {
621        &other.to_str() == self
622    }
623}
624impl<'t, const N: usize> PartialEq<&Flexstr<N>> for &'t str {
625    fn eq(&self, other: &&Flexstr<N>) -> bool {
626        &other.to_str() == self
627    }
628}
629
630impl<const N: usize> core::fmt::Debug for Flexstr<N> {
631    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
632        f.pad(&self.to_str())
633    }
634} // Debug impl
635
636impl<const N: usize> core::fmt::Display for Flexstr<N> {
637    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
638        //write!(f, "{}", self.to_str())
639        f.pad(self.to_str())
640    }
641}
642
643impl<const N: usize> core::fmt::Write for Flexstr<N> {
644    fn write_str(&mut self, s: &str) -> core::fmt::Result {
645        self.push_str(s);
646        Ok(())
647    } //write_str
648} //core::fmt::Write trait
649
650impl<const N: usize> core::convert::From<String> for Flexstr<N> {
651    /// *will consume owned string and convert it to a fixed
652    /// representation if its length is less than N*
653    fn from(s: String) -> Self {
654        if s.len() >= N {
655            Flexstr { inner: owned(s) }
656        } else {
657            Flexstr {
658                inner: fixed(tstr::<N>::from(&s[..])),
659            }
660        }
661    }
662} //from String
663
664impl<const M: usize> Flexstr<M> {
665    /// returns a copy/clone of the string with new fixed capacity N.
666    /// Example:
667    /// ```
668    ///  # use fixedstr::Flexstr;
669    ///  let mut a:Flexstr<4> = Flexstr::from("ab");
670    ///  let mut b:Flexstr<8> = a.resize();
671    ///  b.push_str("cdef");
672    ///  assert!(b.is_fixed());
673    ///  a.push_str("1234");
674    ///  assert!(a.is_owned());
675    /// ```
676    pub fn resize<const N: usize>(&self) -> Flexstr<N> {
677        Flexstr::from(self)
678    }
679}
680
681/* redundant
682impl<const N:usize> Add for &Flexstr<N> {
683  type Output = Flexstr<N>;
684  fn add(self, other:Self) -> Self::Output {
685    match (&self.inner, &other.inner) {
686       (owned(a),b) => {
687         let mut a2 = a.clone();
688         a2.push_str(&other);
689         Flexstr{inner:owned(a2)}
690       },
691       (a,owned(b)) => {
692         let mut a2 = self.clone().to_string();
693         a2.push_str(&other);
694         Flexstr{inner:owned(a2)}
695       },
696       (fixed(a), fixed(b)) if a.len() + b.len() >= N => {
697         let mut a2 = a.to_string();
698         a2.push_str(&b);
699         Flexstr{inner:owned(a2)}
700       },
701       (fixed(a), fixed(b)) => {
702         let mut a2 = *a; //copy
703         a2.push(&b);
704         Flexstr{inner:fixed(a2)}
705       }
706    }//match
707  }
708}//Add
709*/
710
711impl<const N: usize, TA: AsRef<str>> Add<TA> for &Flexstr<N> {
712    type Output = Flexstr<N>;
713    fn add(self, other: TA) -> Self::Output {
714        match (&self.inner, other.as_ref()) {
715            (owned(a), b) => {
716                let mut a2 = a.clone();
717                a2.push_str(other.as_ref());
718                Flexstr { inner: owned(a2) }
719            }
720            (fixed(a), b) if a.len() + b.len() >= N => {
721                let mut a2 = a.to_string();
722                a2.push_str(b);
723                Flexstr { inner: owned(a2) }
724            }
725            (fixed(a), b) => {
726                let mut a2 = *a; //copy
727                a2.push(b);
728                Flexstr { inner: fixed(a2) }
729            }
730        } //match
731    }
732} //Add, Rhs = &str
733
734impl<const N: usize> Add<&Flexstr<N>> for &str {
735    type Output = Flexstr<N>;
736    fn add(self, other: &Flexstr<N>) -> Flexstr<N> {
737        let mut a2 = Flexstr::from(self);
738        a2.push_str(other);
739        a2
740    }
741} //Add &str on left
742
743impl<const N: usize> Add<Flexstr<N>> for &str {
744    type Output = Flexstr<N>;
745    fn add(self, other: Flexstr<N>) -> Flexstr<N> {
746        let mut a2 = Flexstr::from(self);
747        a2.push_str(&other);
748        a2
749    }
750} //Add &str on left
751
752impl<const N: usize> core::hash::Hash for Flexstr<N> {
753    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
754        self.as_ref().hash(state);
755    }
756} //hash
757
758impl<const N: usize> core::cmp::PartialEq for Flexstr<N> {
759    fn eq(&self, other: &Self) -> bool {
760        self.as_ref() == other.as_ref()
761    }
762} //eq
763
764impl<const N: usize> core::str::FromStr for Flexstr<N> {
765    type Err = &'static str;
766    fn from_str(s: &str) -> Result<Self, Self::Err> {
767        Ok(Flexstr::from(s))
768    }
769}
770
771/// convenient type aliases for [Flexstr]
772pub type flexstr8 = Flexstr<8>;
773pub type flexstr16 = Flexstr<16>;
774pub type flexstr32 = Flexstr<32>;
775pub type flexstr64 = Flexstr<64>;
776pub type flexstr128 = Flexstr<128>;
777pub type flexstr256 = Flexstr<256>;