1#![allow(
4 non_camel_case_types,
5 non_snake_case,
6 clippy::missing_safety_doc,
7 clippy::too_many_arguments,
8 clippy::type_complexity,
9 clippy::upper_case_acronyms
10)]
11
12use alloc::borrow::Cow;
13use alloc::string::String;
14use core::ffi::{CStr, c_char};
15use core::fmt;
16use core::hash;
17use core::ops;
18
19#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
21#[repr(transparent)]
22pub struct ByteArray<const N: usize>(pub [u8; N]);
23
24impl<const N: usize> Default for ByteArray<N> {
25 #[inline]
26 fn default() -> Self {
27 Self([0; N])
28 }
29}
30
31impl<const N: usize> ops::Deref for ByteArray<N> {
32 type Target = [u8; N];
33
34 #[inline]
35 fn deref(&self) -> &Self::Target {
36 &self.0
37 }
38}
39
40impl<const N: usize> fmt::Debug for ByteArray<N> {
41 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42 write!(f, "ByteArray<{}>({:?})", N, self.0)
43 }
44}
45
46impl<const N: usize> fmt::Display for ByteArray<N> {
47 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48 write!(f, "{:?}", self.0)
49 }
50}
51
52impl<const N: usize> From<[u8; N]> for ByteArray<N> {
53 #[inline]
54 fn from(array: [u8; N]) -> Self {
55 Self(array)
56 }
57}
58
59impl<const N: usize> From<ByteArray<N>> for [u8; N] {
60 #[inline]
61 fn from(array: ByteArray<N>) -> Self {
62 array.0
63 }
64}
65
66#[derive(Copy, Clone, PartialOrd, Ord)]
92#[repr(transparent)]
93pub struct StringArray<const N: usize>([c_char; N]);
94
95impl<const N: usize> StringArray<N> {
96 #[inline]
102 pub fn new(array: [c_char; N]) -> Self {
103 assert!(array.contains(&0));
104 Self(array)
105 }
106
107 #[inline]
115 pub const fn from_bytes(bytes: &[u8]) -> Self {
116 let mut array = [0; N];
117
118 let mut index = 0;
119 while index < bytes.len() && index + 1 < N {
120 if bytes[index] != 0 {
121 array[index] = bytes[index] as c_char;
122 index += 1;
123 } else {
124 break;
125 }
126 }
127
128 Self(array)
129 }
130
131 #[inline]
137 pub fn from_cstr(cstr: &CStr) -> Self {
138 Self::from_bytes(cstr.to_bytes())
139 }
140
141 #[inline]
152 pub unsafe fn from_ptr(ptr: *const c_char) -> Self {
153 Self::from_cstr(unsafe { CStr::from_ptr(ptr) })
154 }
155
156 #[inline]
158 pub fn as_array(&self) -> &[c_char; N] {
159 &self.0
160 }
161
162 #[inline]
164 pub fn as_bytes(&self) -> &[u8] {
165 unsafe { self.as_array().align_to::<u8>().1 }
166 }
167
168 #[inline]
170 pub fn as_cstr(&self) -> &CStr {
171 let bytes = self.as_bytes();
172 let nul = bytes.iter().position(|b| *b == b'\0');
173 let end = nul.unwrap_or(N - 1);
174 unsafe { CStr::from_bytes_with_nul_unchecked(&bytes[..end + 1]) }
175 }
176
177 #[inline]
179 pub fn to_string_lossy(&self) -> Cow<'_, str> {
180 let bytes = self.as_bytes();
181 let nul = bytes.iter().position(|b| *b == b'\0');
182 let end = nul.unwrap_or(N);
183 String::from_utf8_lossy(&bytes[..end])
184 }
185}
186
187impl<const N: usize> Default for StringArray<N> {
188 #[inline]
189 fn default() -> Self {
190 Self([0; N])
191 }
192}
193
194impl<const N: usize> PartialEq for StringArray<N> {
195 fn eq(&self, other: &Self) -> bool {
196 self.as_cstr() == other.as_cstr()
197 }
198}
199
200impl<const N: usize> Eq for StringArray<N> {}
201
202impl<const N: usize> hash::Hash for StringArray<N> {
203 fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
204 self.as_cstr().hash(hasher);
205 }
206}
207
208impl<const N: usize> ops::Deref for StringArray<N> {
209 type Target = [c_char; N];
210
211 #[inline]
212 fn deref(&self) -> &Self::Target {
213 &self.0
214 }
215}
216
217impl<const N: usize> fmt::Debug for StringArray<N> {
218 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219 write!(f, "StringArray<{}>({:?})", N, self.to_string_lossy())
220 }
221}
222
223impl<const N: usize> fmt::Display for StringArray<N> {
224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225 write!(f, "{}", self.to_string_lossy())
226 }
227}
228
229impl<const N: usize> From<[c_char; N]> for StringArray<N> {
230 #[inline]
231 fn from(array: [c_char; N]) -> Self {
232 Self(array)
233 }
234}
235
236impl<const N: usize> From<StringArray<N>> for [c_char; N] {
237 #[inline]
238 fn from(array: StringArray<N>) -> Self {
239 array.0
240 }
241}
242
243#[cfg(test)]
244mod test {
245 use super::*;
246
247 use std::collections::hash_map::DefaultHasher;
248 use std::hash::{Hash, Hasher};
249
250 fn hash(hash: impl Hash) -> u64 {
251 let mut hasher = DefaultHasher::new();
252 hash.hash(&mut hasher);
253 hasher.finish()
254 }
255
256 #[test]
257 fn test_string_array_from_bytes() {
258 type S1 = StringArray<1>;
259
260 assert_eq!(b"\0", S1::from_bytes(b"").as_bytes());
261 assert_eq!(b"\0", S1::from_bytes(b"\0").as_bytes());
262 assert_eq!(b"\0", S1::from_bytes(b"\0bar").as_bytes());
263
264 assert_eq!(b"\0", S1::from_bytes(b"322").as_bytes());
265 assert_eq!(b"\0", S1::from_bytes(b"322\0").as_bytes());
266 assert_eq!(b"\0", S1::from_bytes(b"322\0bar").as_bytes());
267
268 type S4 = StringArray<4>;
269
270 assert_eq!(b"\0\0\0\0", S4::from_bytes(b"").as_bytes());
271 assert_eq!(b"\0\0\0\0", S4::from_bytes(b"\0").as_bytes());
272 assert_eq!(b"\0\0\0\0", S4::from_bytes(b"\0bar").as_bytes());
273
274 assert_eq!(b"322\0", S4::from_bytes(b"322").as_bytes());
275 assert_eq!(b"322\0", S4::from_bytes(b"322\0").as_bytes());
276 assert_eq!(b"322\0", S4::from_bytes(b"322\0bar").as_bytes());
277
278 assert_eq!(b"128\0", S4::from_bytes(b"1288").as_bytes());
279 assert_eq!(b"128\0", S4::from_bytes(b"1288\0").as_bytes());
280 assert_eq!(b"128\0", S4::from_bytes(b"1288\0bar").as_bytes());
281 }
282
283 #[test]
284 fn test_string_array_cmp() {
285 macro_rules! assert_cmp_eq {
286 ($left:expr, $right:expr) => {
287 assert_eq!($left, $right);
288 assert_eq!(hash($left), hash($right));
289 };
290 }
291
292 macro_rules! assert_cmp_ne {
293 ($left:expr, $right:expr) => {
294 assert_ne!($left, $right);
295 assert_ne!(hash($left), hash($right));
296 };
297 }
298
299 type S32 = StringArray<32>;
300
301 assert_cmp_eq!(S32::from_bytes(b""), S32::from_bytes(b""));
302 assert_cmp_eq!(S32::from_bytes(b"\0"), S32::from_bytes(b""));
303 assert_cmp_eq!(S32::from_bytes(b""), S32::from_bytes(b"\0"));
304 assert_cmp_eq!(S32::from_bytes(b"\0"), S32::from_bytes(b"\0"));
305 assert_cmp_eq!(S32::from_bytes(b"\0foo"), S32::from_bytes(b"\0bar"));
306
307 assert_cmp_eq!(S32::from_bytes(b"322"), S32::from_bytes(b"322"));
308 assert_cmp_eq!(S32::from_bytes(b"322\0"), S32::from_bytes(b"322"));
309 assert_cmp_eq!(S32::from_bytes(b"322"), S32::from_bytes(b"322\0"));
310 assert_cmp_eq!(S32::from_bytes(b"322\0"), S32::from_bytes(b"322\0"));
311 assert_cmp_eq!(S32::from_bytes(b"322\0foo"), S32::from_bytes(b"322\0bar"));
312
313 assert_cmp_ne!(S32::from_bytes(b"322"), S32::from_bytes(b"422"));
314 assert_cmp_ne!(S32::from_bytes(b"322"), S32::from_bytes(b"332"));
315 assert_cmp_ne!(S32::from_bytes(b"322"), S32::from_bytes(b"323"));
316
317 assert_cmp_ne!(S32::from_bytes(b"322"), S32::from_bytes(b"32"));
318 assert_cmp_ne!(S32::from_bytes(b"322"), S32::from_bytes(b"3222"));
319 }
320}