fixedstr_ext/
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        match &self.inner {
162            fixed(s) => s.as_str_safe(),
163            owned(s) => Ok(&s[..]),
164        } //match
165    }
166
167    /// retrieves a copy of the underlying fixed string, if it is a fixed string.
168    /// Note that since the `tstr` type is not exported, this function should
169    /// be used in conjunction with one of the public aliases [str4]-[str256].
170    /// For example,
171    /// ```ignore
172    ///   let s = Flexstr::<8>::from("abcd");
173    ///   let t:str8 = s.get_str().unwrap();
174    /// ```
175    pub fn get_str(&self) -> Option<tstr<N>> {
176        if let fixed(s) = &self.inner {
177            Some(*s)
178        } else {
179            None
180        }
181    } //get_str
182
183    /// if the underlying representation of the string is an owned string,
184    /// return the owned string, leaving an empty string in its place.
185    pub fn take_string(&mut self) -> Option<String> {
186        if let owned(s) = &mut self.inner {
187            let mut temp = fixed(tstr::new());
188            core::mem::swap(&mut self.inner, &mut temp);
189            if let owned(t) = temp {
190                Some(t)
191            } else {
192                None
193            }
194        } else {
195            None
196        }
197    } //take_owned
198
199    /// this function consumes the Flexstr and returns an owned string
200    pub fn to_string(self) -> String {
201        match self.inner {
202            fixed(s) => s.to_string(),
203            owned(s) => s,
204        } //match
205    } //to_string
206
207    /// returns the nth char of the string, if it exists
208    pub fn nth(&self, n: usize) -> Option<char> {
209        self.to_str().chars().nth(n)
210    }
211
212    /// returns the nth byte of the string as a char.  This function
213    /// is designed to be quicker than [Flexstr::nth] and does not check
214    /// for bounds.
215    pub fn nth_bytechar(&self, n: usize) -> char {
216        match &self.inner {
217            fixed(s) => s.nth_ascii(n),
218            owned(s) => s.as_bytes()[n] as char,
219        }
220    } //nth_bytechar
221
222    /// alias for [Self::nth_bytechar] (for backwards compatibility)
223    pub fn nth_ascii(&self, n: usize) -> char {
224        self.nth_bytechar(n)
225    }
226
227    /// returns a u8-slice that represents the underlying string. The first
228    /// byte of the slice is **not** the length of the string regarless of
229    /// the internal representation.
230    pub fn as_bytes(&self) -> &[u8] {
231        match &self.inner {
232            fixed(f) => f.as_bytes(),
233            owned(s) => s.as_bytes(),
234        } //match
235    }
236
237    /// returns mutable u8-slice of string underneath.  Function requires
238    /// call to [String::as_mut_str] and is therefore marked unsafe.
239    pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
240        match &mut self.inner {
241            fixed(f) => f.as_bytes_mut(),
242            owned(s) => s.as_mut_str().as_bytes_mut(),
243        } //match
244    }
245
246    /// changes a character at character position i to c.  This function
247    /// requires that c is in the same character class (ascii or unicode)
248    /// as the char being replaced.  It never shuffles the bytes underneath.
249    /// The function returns true if the change was successful.
250    pub fn set(&mut self, i: usize, c: char) -> bool {
251        match &mut self.inner {
252            fixed(s) => s.set(i, c),
253            owned(s) => unsafe {
254                let ref mut cbuf = [0u8; 4];
255                c.encode_utf8(cbuf);
256                let clen = c.len_utf8();
257                if let Some((bi, rc)) = s.char_indices().nth(i) {
258                    if clen == rc.len_utf8() {
259                        s.as_bytes_mut()[bi..bi + clen].copy_from_slice(&cbuf[..clen]);
260                        //self.chrs[bi + 1..bi + clen + 1].copy_from_slice(&cbuf[..clen]);
261                        //for k in 0..clen {self.chrs[bi+k+1] = cbuf[k];}
262                        return true;
263                    }
264                }
265                return false;
266            },
267        } //match
268    } //set
269
270    /// returns whether the internal representation is a fixed string (tstr)
271    pub fn is_fixed(&self) -> bool {
272        match &self.inner {
273            fixed(_) => true,
274            owned(_) => false,
275        }
276    } //is_fixed
277
278    /// returns whether the internal representation is an owned String
279    pub fn is_owned(&self) -> bool {
280        !self.is_fixed()
281    }
282
283    /// applies the destructive closure only if the internal representation
284    /// is a fixed string
285    pub fn if_fixed<F>(&mut self, f: F)
286    where
287        F: FnOnce(&mut tstr<N>),
288    {
289        if let fixed(s) = &mut self.inner {
290            f(s);
291        }
292    }
293
294    /// applies the destructive closure only if the internal representation
295    /// is a fixed string
296    pub fn if_owned<F>(&mut self, f: F)
297    where
298        F: FnOnce(&mut str),
299    {
300        if let owned(s) = &mut self.inner {
301            f(s);
302        }
303    }
304
305    /// applies closure f if the internal representation is a fixed string,
306    /// or closure g if the internal representation is an owned string.
307    pub fn map_or<F, G, U>(&self, f: F, g: G) -> U
308    where
309        F: FnOnce(&tstr<N>) -> U,
310        G: FnOnce(&str) -> U,
311    {
312        match &self.inner {
313            fixed(s) => f(s),
314            owned(s) => g(&s[..]),
315        } //match
316    } //map
317
318    /// version of [Flexstr::map_or] accepting FnMut closures
319    pub fn map_or_mut<F, G, U>(&mut self, f: &mut F, g: &mut G) -> U
320    where
321        F: FnMut(&mut tstr<N>) -> U,
322        G: FnMut(&mut str) -> U,
323    {
324        match &mut self.inner {
325            fixed(s) => f(s),
326            owned(s) => g(&mut s[..]),
327        } //match
328    } //map
329
330    /// This function will append the Flexstr with the given slice,
331    /// switching to the owned-String representation if necessary.  The function
332    /// returns true if the resulting string uses a `tstr<N>` type, and
333    /// false if the representation is an owned string.
334    pub fn push_str(&mut self, s: &str) -> bool {
335        match &mut self.inner {
336            fixed(fs) if fs.len() + s.len() < N => {
337                fs.push(s);
338                true
339            }
340            fixed(fs) => {
341                let fss = fs.to_string() + s;
342                self.inner = owned(fss);
343                false
344            }
345            owned(ns) => {
346                ns.push_str(s);
347                false
348            }
349        } //match
350    } //push_str
351
352    /// appends string with a single character, switching to the String
353    /// representation if necessary.  Returns true if resulting string
354    /// remains fixed.
355    pub fn push(&mut self, c: char) -> bool {
356        let clen = c.len_utf8();
357        match &mut self.inner {
358            owned(s) => {
359                s.push(c);
360                false
361            }
362            fixed(s) if s.len() + clen >= N => {
363                let mut fss = s.to_string();
364                fss.push(c);
365                self.inner = owned(fss);
366                false
367            }
368            fixed(s) => {
369                let mut buf = [0u8; 4];
370                let bstr = c.encode_utf8(&mut buf);
371                s.push(bstr);
372                true
373            }
374        } //match
375    } //push
376
377    /// alias for push
378    #[inline]
379    pub fn push_char(&mut self, c: char) -> bool {
380        self.push(c)
381    }
382
383    /// remove and return last character in string, if it exists
384    pub fn pop(&mut self) -> Option<char> {
385        if self.len() == 0 {
386            return None;
387        }
388        match &mut self.inner {
389            fixed(s) => s.pop_char(),
390            owned(s) if s.len() > N => s.pop(),
391            owned(s) => {
392                // change representation
393                let lastchar = s.pop();
394                self.inner = fixed(tstr::from(&s));
395                lastchar
396            }
397        } //match
398    } //pop
399
400    /// alias for [Self::pop]
401    pub fn pop_char(&mut self) -> Option<char> {
402        self.pop()
403    }
404
405    /// this function truncates a string at the indicated byte position,
406    /// returning true if the truncated string is fixed, and false if owned.
407    /// The operation has no effect if n is larger than the length of the
408    /// string.  The operation will **panic** if n is not on a character
409    /// boundary, similar to [String::truncate].
410    pub fn truncate(&mut self, n: usize) -> bool {
411        match &mut self.inner {
412            fixed(fs) if n < fs.len() => {
413                fs.truncate_bytes(n);
414                true
415            }
416            fixed(_) => true,
417            owned(s) if n < N => {
418                assert!(s.is_char_boundary(n));
419                self.inner = fixed(tstr::<N>::from(&s[..n]));
420                true
421            }
422            owned(s) => {
423                if n < s.len() {
424                    s.truncate(n);
425                }
426                false
427            }
428        } //match
429    } //truncate
430
431    /// resets string to empty
432    pub fn clear(&mut self) {
433        match &mut self.inner {
434            fixed(s) => {
435                s.clear();
436            }
437            owned(s) => {
438                self.inner = fixed(tstr::default());
439            }
440        }
441    } //clear
442
443    /// returns string corresponding to slice indices as a copy or clone.
444    pub fn substr(&self, start: usize, end: usize) -> Flexstr<N> {
445        match &self.inner {
446            fixed(s) => Flexstr {
447                inner: fixed(s.substr(start, end)),
448            },
449            owned(s) => Self::from(&s[start..end]),
450        }
451    } //substr
452
453    /// Splits the string into a `tstr<N>` portion and a String portion.
454    /// The structure inherits the fixed part and the String returned will
455    /// contain the extra bytes that does not fit.  Example:
456    ///
457    /// ```
458    ///  # use fixedstr_ext::*;
459    ///   let mut fs:Flexstr<4> = Flexstr::from("abcdefg");
460    ///   let extras = fs.split_off();
461    ///   assert!( &fs=="abc" && &extras=="defg" && fs.is_fixed());
462    /// ```
463    pub fn split_off(&mut self) -> String {
464        match &mut self.inner {
465            fixed(s) => String::default(),
466            owned(s) => {
467                let answer = String::from(&s[N - 1..]);
468                self.inner = fixed(tstr::<N>::from(&s[..N - 1]));
469                answer
470            }
471        } //match
472    } //split_off
473
474    /// in-place modification of ascii string to lower-case
475    pub fn make_ascii_lowercase(&mut self) {
476        match &mut self.inner {
477            fixed(s) => {
478                s.make_ascii_lowercase();
479            }
480            owned(s) => {
481                s.as_mut_str().make_ascii_lowercase();
482            }
483        } //match
484    } //make_ascii_lowercase
485
486    /// in-place modification of ascii string to upper-case
487    pub fn make_ascii_uppercase(&mut self) {
488        match &mut self.inner {
489            fixed(s) => {
490                s.make_ascii_uppercase();
491            }
492            owned(s) => {
493                s.as_mut_str().make_ascii_uppercase();
494            }
495        } //match
496    }
497
498    /// Tests for ascii case-insensitive equality with another string.
499    /// This function does not test if either string is ascii.
500    pub fn case_insensitive_eq<TA>(&self, other: TA) -> bool
501    where
502        TA: AsRef<str>,
503    {
504        if self.len() != other.as_ref().len() {
505            return false;
506        }
507        let obytes = other.as_ref().as_bytes();
508        let sbytes = self.as_bytes();
509        for i in 0..self.len() {
510            let mut c = sbytes[i];
511            if (c > 64 && c < 91) {
512                c = c | 32;
513            } // make lowercase
514            let mut d = obytes[i];
515            if (d > 64 && d < 91) {
516                d = d | 32;
517            } // make lowercase
518            if c != d {
519                return false;
520            }
521        } //for
522        true
523    } //case_insensitive_eq
524
525    /// Decodes a UTF-16 encodeded slice. If a decoding error is encountered
526    /// or capacity exceeded, an `Err(s)` is returned where s is the
527    /// the encoded string up to the point of the error.
528    pub fn from_utf16(v: &[u16]) -> Result<Self, Self> {
529        let mut s = Self::new();
530        for c in char::decode_utf16(v.iter().cloned()) {
531            if let Ok(c1) = c {
532                if !s.push_char(c1) {
533                    return Err(s);
534                }
535            } else {
536                return Err(s);
537            }
538        }
539        Ok(s)
540    } //from_utf16
541} //impl<N>
542
543impl<const N: usize> Default for Flexstr<N> {
544    fn default() -> Self {
545        Flexstr {
546            inner: fixed(tstr::<N>::default()),
547        }
548    }
549}
550
551/*
552impl<const N:usize> core::hash::Hash for Flexstr<N>
553{
554  fn hash<H:core::hash::Hasher>(&self, state:&mut H) {
555    self.as_str().hash(state)
556  }
557}//hash
558*/
559
560impl<const N: usize> core::ops::Deref for Flexstr<N> {
561    type Target = str;
562    fn deref(&self) -> &Self::Target {
563        self.to_str()
564    }
565}
566
567impl<T: AsRef<str> + ?Sized, const N: usize> core::convert::From<&T> for Flexstr<N> {
568    fn from(s: &T) -> Self {
569        Self::make(s.as_ref())
570    }
571}
572
573impl<T: AsMut<str> + ?Sized, const N: usize> core::convert::From<&mut T> for Flexstr<N> {
574    fn from(s: &mut T) -> Self {
575        Self::make(s.as_mut())
576    }
577}
578
579impl<const N: usize> core::cmp::PartialOrd for Flexstr<N> {
580    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
581        //Some(self.chrs[0..self.len].cmp(other.chrs[0..other.len]))
582        Some(self.cmp(other))
583    }
584}
585
586impl<const N: usize> core::cmp::Ord for Flexstr<N> {
587    fn cmp(&self, other: &Self) -> Ordering {
588        self.to_str().cmp(other.to_str())
589    }
590}
591
592impl<const N: usize> core::convert::AsRef<str> for Flexstr<N> {
593    fn as_ref(&self) -> &str {
594        self.to_str()
595    }
596}
597impl<const N: usize> core::convert::AsMut<str> for Flexstr<N> {
598    fn as_mut(&mut self) -> &mut str {
599        match &mut self.inner {
600            fixed(f) => f.as_mut(),
601            owned(s) => s.as_mut(),
602        } //match
603    }
604}
605
606impl<const N: usize> PartialEq<&str> for Flexstr<N> {
607    fn eq(&self, other: &&str) -> bool {
608        &self.to_str() == other // see below
609    } //eq
610}
611
612impl<const N: usize> PartialEq<&str> for &Flexstr<N> {
613    fn eq(&self, other: &&str) -> bool {
614        &self.to_str() == other
615    } //eq
616}
617impl<'t, const N: usize> PartialEq<Flexstr<N>> for &'t str {
618    fn eq(&self, other: &Flexstr<N>) -> bool {
619        &other.to_str() == self
620    }
621}
622impl<'t, const N: usize> PartialEq<&Flexstr<N>> for &'t str {
623    fn eq(&self, other: &&Flexstr<N>) -> bool {
624        &other.to_str() == self
625    }
626}
627
628impl<const N: usize> core::fmt::Debug for Flexstr<N> {
629    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
630        f.pad(&self.to_str())
631    }
632} // Debug impl
633
634impl<const N: usize> core::fmt::Display for Flexstr<N> {
635    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
636        //write!(f, "{}", self.to_str())
637        f.pad(self.to_str())
638    }
639}
640
641impl<const N: usize> core::fmt::Write for Flexstr<N> {
642    fn write_str(&mut self, s: &str) -> core::fmt::Result {
643        self.push_str(s);
644        Ok(())
645    } //write_str
646} //core::fmt::Write trait
647
648impl<const N: usize> core::convert::From<String> for Flexstr<N> {
649    /// *will consume owned string and convert it to a fixed
650    /// representation if its length is less than N*
651    fn from(s: String) -> Self {
652        if s.len() >= N {
653            Flexstr { inner: owned(s) }
654        } else {
655            Flexstr {
656                inner: fixed(tstr::<N>::from(&s[..])),
657            }
658        }
659    }
660} //from String
661
662impl<const M: usize> Flexstr<M> {
663    /// returns a copy/clone of the string with new fixed capacity N.
664    /// Example:
665    /// ```
666    ///  # use fixedstr_ext::Flexstr;
667    ///  let mut a:Flexstr<4> = Flexstr::from("ab");
668    ///  let mut b:Flexstr<8> = a.resize();
669    ///  b.push_str("cdef");
670    ///  assert!(b.is_fixed());
671    ///  a.push_str("1234");
672    ///  assert!(a.is_owned());
673    /// ```
674    pub fn resize<const N: usize>(&self) -> Flexstr<N> {
675        Flexstr::from(self)
676    }
677}
678
679/* redundant
680impl<const N:usize> Add for &Flexstr<N> {
681  type Output = Flexstr<N>;
682  fn add(self, other:Self) -> Self::Output {
683    match (&self.inner, &other.inner) {
684       (owned(a),b) => {
685         let mut a2 = a.clone();
686         a2.push_str(&other);
687         Flexstr{inner:owned(a2)}
688       },
689       (a,owned(b)) => {
690         let mut a2 = self.clone().to_string();
691         a2.push_str(&other);
692         Flexstr{inner:owned(a2)}
693       },
694       (fixed(a), fixed(b)) if a.len() + b.len() >= N => {
695         let mut a2 = a.to_string();
696         a2.push_str(&b);
697         Flexstr{inner:owned(a2)}
698       },
699       (fixed(a), fixed(b)) => {
700         let mut a2 = *a; //copy
701         a2.push(&b);
702         Flexstr{inner:fixed(a2)}
703       }
704    }//match
705  }
706}//Add
707*/
708
709impl<const N: usize, TA: AsRef<str>> Add<TA> for &Flexstr<N> {
710    type Output = Flexstr<N>;
711    fn add(self, other: TA) -> Self::Output {
712        match (&self.inner, other.as_ref()) {
713            (owned(a), b) => {
714                let mut a2 = a.clone();
715                a2.push_str(other.as_ref());
716                Flexstr { inner: owned(a2) }
717            }
718            (fixed(a), b) if a.len() + b.len() >= N => {
719                let mut a2 = a.to_string();
720                a2.push_str(b);
721                Flexstr { inner: owned(a2) }
722            }
723            (fixed(a), b) => {
724                let mut a2 = *a; //copy
725                a2.push(b);
726                Flexstr { inner: fixed(a2) }
727            }
728        } //match
729    }
730} //Add, Rhs = &str
731
732impl<const N: usize> Add<&Flexstr<N>> for &str {
733    type Output = Flexstr<N>;
734    fn add(self, other: &Flexstr<N>) -> Flexstr<N> {
735        let mut a2 = Flexstr::from(self);
736        a2.push_str(other);
737        a2
738    }
739} //Add &str on left
740
741impl<const N: usize> Add<Flexstr<N>> for &str {
742    type Output = Flexstr<N>;
743    fn add(self, other: Flexstr<N>) -> Flexstr<N> {
744        let mut a2 = Flexstr::from(self);
745        a2.push_str(&other);
746        a2
747    }
748} //Add &str on left
749
750impl<const N: usize> core::hash::Hash for Flexstr<N> {
751    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
752        self.as_ref().hash(state);
753    }
754} //hash
755
756impl<const N: usize> core::cmp::PartialEq for Flexstr<N> {
757    fn eq(&self, other: &Self) -> bool {
758        self.as_ref() == other.as_ref()
759    }
760} //eq
761
762impl<const N: usize> core::str::FromStr for Flexstr<N> {
763    type Err = &'static str;
764    fn from_str(s: &str) -> Result<Self, Self::Err> {
765        Ok(Flexstr::from(s))
766    }
767}
768
769/// convenient type aliases for [Flexstr]
770pub type flexstr8 = Flexstr<8>;
771pub type flexstr16 = Flexstr<16>;
772pub type flexstr32 = Flexstr<32>;
773pub type flexstr64 = Flexstr<64>;
774pub type flexstr128 = Flexstr<128>;
775pub type flexstr256 = Flexstr<256>;