diff --git a/src/inline.rs b/src/inline.rs index 5d66fb7..30b569b 100644 --- a/src/inline.rs +++ b/src/inline.rs @@ -63,22 +63,35 @@ impl + AsMut<[u8]> + Default + TypeSize> InlineStrin StrRepr::default().as_ref().len() } - pub fn from_str(val: &str) -> Option { + #[inline] + fn from_len_and_write(len: usize, write: impl FnOnce(&mut [u8])) -> Option { let mut arr = StrRepr::default(); - if val.len() > size_of::() { + if len > size_of::() { return None; } - arr.as_mut()[..val.len()].copy_from_slice(val.as_bytes()); + write(arr.as_mut()); - if val.len() != Self::max_len() { + if len != Self::max_len() { // 0xFF terminate the string, to gain an extra inline character - arr.as_mut()[val.len()] = Self::TERMINATOR; + arr.as_mut()[len] = Self::TERMINATOR; } Some(Self { arr }) } + pub fn from_str(val: &str) -> Option { + Self::from_len_and_write(val.len(), |arr| { + arr[..val.len()].copy_from_slice(val.as_bytes()); + }) + } + + pub fn from_char(val: char) -> Option { + Self::from_len_and_write(val.len_utf8(), |arr| { + val.encode_utf8(arr); + }) + } + pub fn len(&self) -> u8 { // Copy to a temporary, 16 byte array to allow for SIMD impl. let mut buf = [0_u8; 16]; diff --git a/src/string.rs b/src/string.rs index 93adfd1..907dee1 100644 --- a/src/string.rs +++ b/src/string.rs @@ -294,6 +294,26 @@ impl TryFrom for FixedString { } } +impl From for FixedString { + fn from(value: char) -> Self { + use alloc::vec; + + if let Some(value) = InlineString::from_char(value) { + return Self(FixedStringRepr::Inline(value)); + } + + let mut bytes = vec![0; value.len_utf8()].into_boxed_slice(); + + value.encode_utf8(&mut bytes); + + let bytes = bytes + .try_into() + .expect("len_utf8 is at most 4, so it will fit in u8"); + + Self(FixedStringRepr::Heap(bytes)) + } +} + impl From> for String { fn from(value: FixedString) -> Self { match value.0 { @@ -474,4 +494,63 @@ mod test { assert_eq!(core::mem::size_of::>(), 13); assert_eq!(core::mem::align_of::>(), 1); } + + #[test] + fn from_char_u8() { + let s: FixedString = 'a'.into(); + assert_eq!(s.len(), 1); + assert!(s.is_inline()); + + let s: FixedString = '¼'.into(); + assert_eq!(s.len(), 2); + assert!(s.is_inline()); + + let s: FixedString = '⚡'.into(); + assert_eq!(s.len(), 3); + assert!(s.is_inline()); + + let s: FixedString = '🦀'.into(); + assert_eq!(s.len(), 4); + #[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))] + assert!(s.is_inline()); + } + + #[test] + fn from_char_u16() { + let s: FixedString = 'a'.into(); + assert_eq!(s.len(), 1); + assert!(s.is_inline()); + + let s: FixedString = '¼'.into(); + assert_eq!(s.len(), 2); + assert!(s.is_inline()); + + let s: FixedString = '⚡'.into(); + assert_eq!(s.len(), 3); + assert!(s.is_inline()); + + let s: FixedString = '🦀'.into(); + assert_eq!(s.len(), 4); + assert!(s.is_inline()); + } + + #[test] + #[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))] + fn from_char_u32() { + let s: FixedString = 'a'.into(); + assert_eq!(s.len(), 1); + assert!(s.is_inline()); + + let s: FixedString = '¼'.into(); + assert_eq!(s.len(), 2); + assert!(s.is_inline()); + + let s: FixedString = '⚡'.into(); + assert_eq!(s.len(), 3); + assert!(s.is_inline()); + + let s: FixedString = '🦀'.into(); + assert_eq!(s.len(), 4); + assert!(s.is_inline()); + } }