|
1 | 1 | use alloc::{ |
2 | 2 | borrow::{Cow, ToOwned}, |
3 | 3 | boxed::Box, |
4 | | - string::{String}, |
| 4 | + string::String, |
5 | 5 | sync::Arc, |
6 | 6 | }; |
7 | 7 | use core::{borrow::Borrow, hash::Hash, str::FromStr}; |
@@ -29,13 +29,17 @@ fn truncate_string(err: InvalidStrLength, max_len: usize) -> String { |
29 | 29 |
|
30 | 30 | #[cold] |
31 | 31 | fn truncate_str(string: &str, max_len: usize) -> &str { |
| 32 | + if string.len() <= max_len { |
| 33 | + return string; |
| 34 | + } |
| 35 | + |
32 | 36 | for len in (0..=max_len).rev() { |
33 | 37 | if string.is_char_boundary(len) { |
34 | 38 | return &string[..len]; |
35 | 39 | } |
36 | 40 | } |
37 | 41 |
|
38 | | - unreachable!("Len 0 is a char boundary") |
| 42 | + unreachable!("Len 0 is a char boundary"); |
39 | 43 | } |
40 | 44 |
|
41 | 45 | /// A fixed size String with length provided at creation denoted in [`ValidLength`], by default [`u32`]. |
@@ -93,7 +97,7 @@ impl<LenT: ValidLength> FixedString<LenT> { |
93 | 97 | /// For lossless fallible conversion, convert to [`Box<str>`] using [`String::into_boxed_str`] and use [`TryFrom`]. |
94 | 98 | #[must_use] |
95 | 99 | pub fn from_string_trunc(str: String) -> Self { |
96 | | - match str.into_boxed_str().try_into() { |
| 100 | + match str.try_into() { |
97 | 101 | Ok(val) => val, |
98 | 102 | Err(err) => Self::from_string_trunc(truncate_string(err, LenT::MAX.to_usize())), |
99 | 103 | } |
@@ -438,6 +442,21 @@ mod test { |
438 | 442 | } |
439 | 443 | } |
440 | 444 |
|
| 445 | + // primarily intended to ensure no hangs occur |
| 446 | + fn check_u32_partial_roundtrip_generic(to_fixed: fn(String) -> FixedString<u32>) { |
| 447 | + for i in 0..=400u32 { |
| 448 | + let original = "a".repeat(i as usize); |
| 449 | + let fixed = to_fixed(original); |
| 450 | + |
| 451 | + assert!(fixed.bytes().all(|c| c == b'a')); |
| 452 | + assert_eq!(fixed.len(), i); |
| 453 | + |
| 454 | + if !fixed.is_static() { |
| 455 | + assert_eq!(fixed.is_inline(), fixed.len() <= 12); |
| 456 | + } |
| 457 | + } |
| 458 | + } |
| 459 | + |
441 | 460 | #[test] |
442 | 461 | fn test_truncating_behaviour() { |
443 | 462 | const STR: &str = "______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________🦀"; |
@@ -482,6 +501,21 @@ mod test { |
482 | 501 | }); |
483 | 502 | } |
484 | 503 |
|
| 504 | + #[test] |
| 505 | + fn check_u32_partial_roundtrip() { |
| 506 | + check_u32_partial_roundtrip_generic(|original| { |
| 507 | + FixedString::<u32>::try_from(original).unwrap() |
| 508 | + }); |
| 509 | + } |
| 510 | + |
| 511 | + #[test] |
| 512 | + fn check_u32_partial_roundtrip_static() { |
| 513 | + check_u32_partial_roundtrip_generic(|original| { |
| 514 | + let static_str = Box::leak(original.into_boxed_str()); |
| 515 | + FixedString::from_static_trunc(static_str) |
| 516 | + }); |
| 517 | + } |
| 518 | + |
485 | 519 | #[test] |
486 | 520 | #[cfg(feature = "serde")] |
487 | 521 | fn check_u8_roundtrip_serde() { |
|
0 commit comments