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