1use core::fmt::{self};
2use core::str;
3
4#[derive(Clone, Copy, PartialEq, Eq)]
13pub struct LiteStr<const N: usize> {
14 bytes: [u8; N],
15}
16
17impl<const N: usize> Default for LiteStr<N> {
18 #[inline(always)]
19 fn default() -> Self {
20 Self { bytes: [0; N] }
21 }
22}
23
24impl<const N: usize> LiteStr<N> {
25 pub const SIZE: usize = N;
26
27 #[inline(never)]
29 pub fn from_str(s: &str) -> Self {
30 let mut bytes = [0u8; N];
31 copy_valid_utf8_prefix(&mut bytes, s.as_bytes(), N);
32 Self { bytes }
33 }
34
35 #[inline(always)]
37 pub fn as_str(&self) -> Result<&str, LiteStrErr> {
38 str::from_utf8(&self.bytes[..find_first_nul(&self.bytes)])
39 .map_err(|_| LiteStrErr::CorruptedData)
40 }
41
42 pub fn from_bytes(bytes: &[u8]) -> Result<Self, LiteStrErr> {
43 if bytes.len() != N {
44 return Err(LiteStrErr::WrongLen);
45 }
46 let mut arr = [0u8; N];
47 arr.copy_from_slice(bytes);
48 validate_filled_buffer(&arr)?;
49 Ok(Self { bytes: arr })
50 }
51
52 #[inline(always)]
53 pub fn to_bytes(&self) -> [u8; N] {
54 self.bytes
55 }
56
57 #[inline(always)]
59 pub fn as_bytes(&self) -> &[u8] {
60 &self.bytes[..find_first_nul(&self.bytes)]
61 }
62
63 #[inline(always)]
65 pub fn len(&self) -> usize {
66 find_first_nul(&self.bytes)
67 }
68}
69
70impl<const N: usize> fmt::Write for LiteStr<N> {
71 #[inline(never)]
72 fn write_str(&mut self, s: &str) -> fmt::Result {
73 let current = self.len();
74 let remaining = N.saturating_sub(current);
75 if remaining == 0 {
76 return Ok(());
77 }
78
79 copy_valid_utf8_prefix(&mut self.bytes[current..], s.as_bytes(), remaining);
80 Ok(())
81 }
82}
83
84impl<const N: usize> fmt::Debug for LiteStr<N> {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self.as_str() {
87 Ok(s) => write!(f, "{:?}", s),
88 Err(_) => f.write_str("LiteStr(<invalid utf-8>)"),
89 }
90 }
91}
92
93#[cfg(feature = "serde")]
94impl<const N: usize> serde::Serialize for LiteStr<N> {
95 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
96 where
97 S: serde::Serializer,
98 {
99 self.as_str()
100 .map_err(serde::ser::Error::custom)?
101 .serialize(serializer)
102 }
103}
104
105#[cfg(feature = "serde")]
106impl<'de, const N: usize> serde::Deserialize<'de> for LiteStr<N> {
107 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
108 where
109 D: serde::Deserializer<'de>,
110 {
111 let s: &str = serde::Deserialize::deserialize(deserializer)?;
112 Ok(LiteStr::from_str(s))
113 }
114}
115
116#[inline(never)]
117fn find_first_nul(bytes: &[u8]) -> usize {
118 bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len())
119}
120
121#[inline(never)]
122fn validate_filled_buffer(bytes: &[u8]) -> Result<(), LiteStrErr> {
123 let len = find_first_nul(bytes);
124
125 if str::from_utf8(&bytes[..len]).is_err() {
126 return Err(LiteStrErr::InvalidUtf8);
127 }
128
129 if len < bytes.len() && bytes[len..].iter().any(|&b| b != 0) {
130 return Err(LiteStrErr::CorruptedData);
131 }
132
133 Ok(())
134}
135
136#[inline(never)]
137fn copy_valid_utf8_prefix(dst: &mut [u8], src: &[u8], max_len: usize) -> usize {
138 let len = src.len().min(max_len);
139 match str::from_utf8(&src[..len]) {
140 Ok(_) => {
141 dst[..len].copy_from_slice(&src[..len]);
142 len
143 }
144 Err(e) => {
145 let valid = e.valid_up_to();
146 dst[..valid].copy_from_slice(&src[..valid]);
147 valid
148 }
149 }
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub enum LiteStrErr {
155 WrongLen,
157 InvalidUtf8,
159 CorruptedData,
161}
162
163impl fmt::Display for LiteStrErr {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 match self {
166 LiteStrErr::WrongLen => f.write_str("input len does not match SIZE"),
167 LiteStrErr::InvalidUtf8 => f.write_str("input is not valid UTF-8"),
168 LiteStrErr::CorruptedData => {
169 f.write_str("internal data is corrupted or violates the representation invariant")
170 }
171 }
172 }
173}
174
175impl core::error::Error for LiteStrErr {}