1 | // This file is part of ICU4X. For terms of use, please see the file |
2 | // called LICENSE at the top level of the ICU4X source tree |
3 | // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). |
4 | |
5 | use core::iter::FromIterator; |
6 | |
7 | use alloc::boxed::Box; |
8 | use alloc::vec; |
9 | use alloc::vec::Vec; |
10 | use core::ops::{Deref, DerefMut}; |
11 | use litemap::store::*; |
12 | |
13 | /// Internal: A vector that supports no-allocation, constant values if length 0 or 1. |
14 | /// Using ZeroOne(Option<T>) saves 8 bytes in ShortSlice via niche optimization. |
15 | #[derive (Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
16 | pub(crate) enum ShortSlice<T> { |
17 | ZeroOne(Option<T>), |
18 | Multi(Box<[T]>), |
19 | } |
20 | |
21 | impl<T> ShortSlice<T> { |
22 | #[inline ] |
23 | pub const fn new() -> Self { |
24 | Self::ZeroOne(None) |
25 | } |
26 | |
27 | #[inline ] |
28 | pub const fn new_single(item: T) -> Self { |
29 | Self::ZeroOne(Some(item)) |
30 | } |
31 | |
32 | pub fn push(&mut self, item: T) { |
33 | *self = match core::mem::replace(self, Self::ZeroOne(None)) { |
34 | ShortSlice::ZeroOne(None) => ShortSlice::ZeroOne(Some(item)), |
35 | ShortSlice::ZeroOne(Some(prev_item)) => { |
36 | ShortSlice::Multi(vec![prev_item, item].into_boxed_slice()) |
37 | } |
38 | ShortSlice::Multi(items) => { |
39 | let mut items = items.into_vec(); |
40 | items.push(item); |
41 | ShortSlice::Multi(items.into_boxed_slice()) |
42 | } |
43 | }; |
44 | } |
45 | |
46 | #[inline ] |
47 | pub const fn single(&self) -> Option<&T> { |
48 | match self { |
49 | ShortSlice::ZeroOne(Some(v)) => Some(v), |
50 | _ => None, |
51 | } |
52 | } |
53 | |
54 | #[inline ] |
55 | pub fn len(&self) -> usize { |
56 | match self { |
57 | ShortSlice::ZeroOne(None) => 0, |
58 | ShortSlice::ZeroOne(_) => 1, |
59 | ShortSlice::Multi(ref v) => v.len(), |
60 | } |
61 | } |
62 | |
63 | pub fn insert(&mut self, index: usize, elt: T) { |
64 | assert!( |
65 | index <= self.len(), |
66 | "insertion index (is {}) should be <= len (is {})" , |
67 | index, |
68 | self.len() |
69 | ); |
70 | |
71 | *self = match core::mem::replace(self, ShortSlice::ZeroOne(None)) { |
72 | ShortSlice::ZeroOne(None) => ShortSlice::ZeroOne(Some(elt)), |
73 | ShortSlice::ZeroOne(Some(item)) => { |
74 | let items = if index == 0 { |
75 | vec![elt, item].into_boxed_slice() |
76 | } else { |
77 | vec![item, elt].into_boxed_slice() |
78 | }; |
79 | ShortSlice::Multi(items) |
80 | } |
81 | ShortSlice::Multi(items) => { |
82 | let mut items = items.into_vec(); |
83 | items.insert(index, elt); |
84 | ShortSlice::Multi(items.into_boxed_slice()) |
85 | } |
86 | } |
87 | } |
88 | |
89 | pub fn remove(&mut self, index: usize) -> T { |
90 | assert!( |
91 | index < self.len(), |
92 | "removal index (is {}) should be < len (is {})" , |
93 | index, |
94 | self.len() |
95 | ); |
96 | |
97 | let (replaced, removed_item) = match core::mem::replace(self, ShortSlice::ZeroOne(None)) { |
98 | ShortSlice::ZeroOne(None) => unreachable!(), |
99 | ShortSlice::ZeroOne(Some(v)) => (ShortSlice::ZeroOne(None), v), |
100 | ShortSlice::Multi(v) => { |
101 | let mut v = v.into_vec(); |
102 | let removed_item = v.remove(index); |
103 | match v.len() { |
104 | #[allow (clippy::unwrap_used)] |
105 | // we know that the vec has exactly one element left |
106 | 1 => (ShortSlice::ZeroOne(Some(v.pop().unwrap())), removed_item), |
107 | // v has at least 2 elements, create a Multi variant |
108 | _ => (ShortSlice::Multi(v.into_boxed_slice()), removed_item), |
109 | } |
110 | } |
111 | }; |
112 | *self = replaced; |
113 | removed_item |
114 | } |
115 | |
116 | #[inline ] |
117 | pub fn clear(&mut self) { |
118 | let _ = core::mem::replace(self, ShortSlice::ZeroOne(None)); |
119 | } |
120 | |
121 | pub fn retain<F>(&mut self, mut f: F) |
122 | where |
123 | F: FnMut(&T) -> bool, |
124 | { |
125 | *self = match core::mem::take(self) { |
126 | Self::ZeroOne(Some(one)) if f(&one) => Self::ZeroOne(Some(one)), |
127 | Self::ZeroOne(_) => Self::ZeroOne(None), |
128 | Self::Multi(slice) => { |
129 | let mut vec = slice.into_vec(); |
130 | vec.retain(f); |
131 | Self::from(vec) |
132 | } |
133 | }; |
134 | } |
135 | } |
136 | |
137 | impl<T> Deref for ShortSlice<T> { |
138 | type Target = [T]; |
139 | |
140 | fn deref(&self) -> &Self::Target { |
141 | match self { |
142 | ShortSlice::ZeroOne(None) => &[], |
143 | ShortSlice::ZeroOne(Some(v: &T)) => core::slice::from_ref(v), |
144 | ShortSlice::Multi(v: &Box<[T]>) => v, |
145 | } |
146 | } |
147 | } |
148 | |
149 | impl<T> DerefMut for ShortSlice<T> { |
150 | fn deref_mut(&mut self) -> &mut Self::Target { |
151 | match self { |
152 | ShortSlice::ZeroOne(None) => &mut [], |
153 | ShortSlice::ZeroOne(Some(v: &mut T)) => core::slice::from_mut(v), |
154 | ShortSlice::Multi(v: &mut Box<[T]>) => v, |
155 | } |
156 | } |
157 | } |
158 | |
159 | impl<T> From<Vec<T>> for ShortSlice<T> { |
160 | fn from(v: Vec<T>) -> Self { |
161 | match v.len() { |
162 | 0 => ShortSlice::ZeroOne(None), |
163 | #[allow (clippy::unwrap_used)] // we know that the vec is not empty |
164 | 1 => ShortSlice::ZeroOne(Some(v.into_iter().next().unwrap())), |
165 | _ => ShortSlice::Multi(v.into_boxed_slice()), |
166 | } |
167 | } |
168 | } |
169 | |
170 | impl<T> Default for ShortSlice<T> { |
171 | fn default() -> Self { |
172 | ShortSlice::ZeroOne(None) |
173 | } |
174 | } |
175 | |
176 | impl<T> FromIterator<T> for ShortSlice<T> { |
177 | fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { |
178 | let mut iter: ::IntoIter = iter.into_iter(); |
179 | match (iter.next(), iter.next()) { |
180 | (Some(first: T), Some(second: T)) => { |
181 | // Size hint behaviour same as `Vec::extend` + 2 |
182 | let mut vec: Vec = Vec::with_capacity(iter.size_hint().0.saturating_add(3)); |
183 | vec.push(first); |
184 | vec.push(second); |
185 | vec.extend(iter); |
186 | Self::Multi(vec.into_boxed_slice()) |
187 | } |
188 | (first: Option, _) => Self::ZeroOne(first), |
189 | } |
190 | } |
191 | } |
192 | |
193 | impl<K, V> StoreConstEmpty<K, V> for ShortSlice<(K, V)> { |
194 | const EMPTY: ShortSlice<(K, V)> = ShortSlice::ZeroOne(None); |
195 | } |
196 | |
197 | impl<K, V> Store<K, V> for ShortSlice<(K, V)> { |
198 | #[inline ] |
199 | fn lm_len(&self) -> usize { |
200 | self.len() |
201 | } |
202 | |
203 | #[inline ] |
204 | fn lm_is_empty(&self) -> bool { |
205 | matches!(self, ShortSlice::ZeroOne(None)) |
206 | } |
207 | |
208 | #[inline ] |
209 | fn lm_get(&self, index: usize) -> Option<(&K, &V)> { |
210 | self.get(index).map(|elt| (&elt.0, &elt.1)) |
211 | } |
212 | |
213 | #[inline ] |
214 | fn lm_last(&self) -> Option<(&K, &V)> { |
215 | match self { |
216 | ShortSlice::ZeroOne(v) => v.as_ref(), |
217 | ShortSlice::Multi(v) => v.last(), |
218 | } |
219 | .map(|elt| (&elt.0, &elt.1)) |
220 | } |
221 | |
222 | #[inline ] |
223 | fn lm_binary_search_by<F>(&self, mut cmp: F) -> Result<usize, usize> |
224 | where |
225 | F: FnMut(&K) -> core::cmp::Ordering, |
226 | { |
227 | self.binary_search_by(|(k, _)| cmp(k)) |
228 | } |
229 | } |
230 | |
231 | impl<K: Ord, V> StoreFromIterable<K, V> for ShortSlice<(K, V)> { |
232 | fn lm_sort_from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self { |
233 | let v: Vec<(K, V)> = Vec::lm_sort_from_iter(iter); |
234 | v.into() |
235 | } |
236 | } |
237 | |
238 | impl<K, V> StoreMut<K, V> for ShortSlice<(K, V)> { |
239 | fn lm_with_capacity(_capacity: usize) -> Self { |
240 | ShortSlice::ZeroOne(None) |
241 | } |
242 | |
243 | fn lm_reserve(&mut self, _additional: usize) {} |
244 | |
245 | fn lm_get_mut(&mut self, index: usize) -> Option<(&K, &mut V)> { |
246 | self.get_mut(index).map(|elt| (&elt.0, &mut elt.1)) |
247 | } |
248 | |
249 | fn lm_push(&mut self, key: K, value: V) { |
250 | self.push((key, value)) |
251 | } |
252 | |
253 | fn lm_insert(&mut self, index: usize, key: K, value: V) { |
254 | self.insert(index, (key, value)) |
255 | } |
256 | |
257 | fn lm_remove(&mut self, index: usize) -> (K, V) { |
258 | self.remove(index) |
259 | } |
260 | |
261 | fn lm_clear(&mut self) { |
262 | self.clear(); |
263 | } |
264 | |
265 | fn lm_retain<F>(&mut self, mut predicate: F) |
266 | where |
267 | F: FnMut(&K, &V) -> bool, |
268 | { |
269 | self.retain(|(k, v)| predicate(k, v)) |
270 | } |
271 | } |
272 | |
273 | impl<'a, K: 'a, V: 'a> StoreIterable<'a, K, V> for ShortSlice<(K, V)> { |
274 | type KeyValueIter = |
275 | core::iter::Map<core::slice::Iter<'a, (K, V)>, for<'r> fn(&'r (K, V)) -> (&'r K, &'r V)>; |
276 | |
277 | fn lm_iter(&'a self) -> Self::KeyValueIter { |
278 | self.iter().map(|elt: &(K, V)| (&elt.0, &elt.1)) |
279 | } |
280 | } |
281 | |
282 | impl<K, V> StoreFromIterator<K, V> for ShortSlice<(K, V)> {} |
283 | |
284 | #[test ] |
285 | fn test_short_slice_impl() { |
286 | litemap::testing::check_store::<ShortSlice<(u32, u64)>>(); |
287 | } |
288 | |
289 | macro_rules! impl_tinystr_subtag { |
290 | ( |
291 | $(#[$doc:meta])* |
292 | $name:ident, |
293 | $($path:ident)::+, |
294 | $macro_name:ident, |
295 | $legacy_macro_name:ident, |
296 | $len_start:literal..=$len_end:literal, |
297 | $tinystr_ident:ident, |
298 | $validate:expr, |
299 | $normalize:expr, |
300 | $is_normalized:expr, |
301 | $error:ident, |
302 | [$good_example:literal $(,$more_good_examples:literal)*], |
303 | [$bad_example:literal $(, $more_bad_examples:literal)*], |
304 | ) => { |
305 | #[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Copy)] |
306 | #[cfg_attr(feature = "serde" , derive(serde::Serialize))] |
307 | #[repr(transparent)] |
308 | $(#[$doc])* |
309 | pub struct $name(tinystr::TinyAsciiStr<$len_end>); |
310 | |
311 | impl $name { |
312 | /// A constructor which takes a UTF-8 slice, parses it and |
313 | #[doc = concat!("produces a well-formed [`" , stringify!($name), "`]." )] |
314 | /// |
315 | /// # Examples |
316 | /// |
317 | /// ``` |
318 | #[doc = concat!("use icu_locid::" , stringify!($($path::)+), stringify!($name), ";" )] |
319 | /// |
320 | #[doc = concat!("assert!(" , stringify!($name), "::try_from_bytes(b" , stringify!($good_example), ").is_ok());" )] |
321 | #[doc = concat!("assert!(" , stringify!($name), "::try_from_bytes(b" , stringify!($bad_example), ").is_err());" )] |
322 | /// ``` |
323 | pub const fn try_from_bytes(v: &[u8]) -> Result<Self, crate::parser::errors::ParserError> { |
324 | Self::try_from_bytes_manual_slice(v, 0, v.len()) |
325 | } |
326 | |
327 | /// Equivalent to [`try_from_bytes(bytes[start..end])`](Self::try_from_bytes), |
328 | /// but callable in a `const` context (which range indexing is not). |
329 | pub const fn try_from_bytes_manual_slice( |
330 | v: &[u8], |
331 | start: usize, |
332 | end: usize, |
333 | ) -> Result<Self, crate::parser::errors::ParserError> { |
334 | let slen = end - start; |
335 | |
336 | #[allow(clippy::double_comparisons)] // if len_start == len_end |
337 | if slen < $len_start || slen > $len_end { |
338 | return Err(crate::parser::errors::ParserError::$error); |
339 | } |
340 | |
341 | match tinystr::TinyAsciiStr::from_bytes_manual_slice(v, start, end) { |
342 | Ok($tinystr_ident) if $validate => Ok(Self($normalize)), |
343 | _ => Err(crate::parser::errors::ParserError::$error), |
344 | } |
345 | } |
346 | |
347 | #[doc = concat!("Safely creates a [`" , stringify!($name), "`] from its raw format" )] |
348 | /// as returned by [`Self::into_raw`]. Unlike [`Self::try_from_bytes`], |
349 | /// this constructor only takes normalized values. |
350 | pub const fn try_from_raw( |
351 | v: [u8; $len_end], |
352 | ) -> Result<Self, crate::parser::errors::ParserError> { |
353 | if let Ok($tinystr_ident) = tinystr::TinyAsciiStr::<$len_end>::try_from_raw(v) { |
354 | if $tinystr_ident.len() >= $len_start && $is_normalized { |
355 | Ok(Self($tinystr_ident)) |
356 | } else { |
357 | Err(crate::parser::errors::ParserError::$error) |
358 | } |
359 | } else { |
360 | Err(crate::parser::errors::ParserError::$error) |
361 | } |
362 | } |
363 | |
364 | #[doc = concat!("Unsafely creates a [`" , stringify!($name), "`] from its raw format" )] |
365 | /// as returned by [`Self::into_raw`]. Unlike [`Self::try_from_bytes`], |
366 | /// this constructor only takes normalized values. |
367 | /// |
368 | /// # Safety |
369 | /// |
370 | /// This function is safe iff [`Self::try_from_raw`] returns an `Ok`. This is the case |
371 | /// for inputs that are correctly normalized. |
372 | pub const unsafe fn from_raw_unchecked(v: [u8; $len_end]) -> Self { |
373 | Self(tinystr::TinyAsciiStr::from_bytes_unchecked(v)) |
374 | } |
375 | |
376 | /// Deconstructs into a raw format to be consumed by |
377 | /// [`from_raw_unchecked`](Self::from_raw_unchecked()) or |
378 | /// [`try_from_raw`](Self::try_from_raw()). |
379 | pub const fn into_raw(self) -> [u8; $len_end] { |
380 | *self.0.all_bytes() |
381 | } |
382 | |
383 | #[inline] |
384 | /// A helper function for displaying as a `&str`. |
385 | pub const fn as_str(&self) -> &str { |
386 | self.0.as_str() |
387 | } |
388 | |
389 | #[doc(hidden)] |
390 | pub const fn into_tinystr(&self) -> tinystr::TinyAsciiStr<$len_end> { |
391 | self.0 |
392 | } |
393 | |
394 | /// Compare with BCP-47 bytes. |
395 | /// |
396 | /// The return value is equivalent to what would happen if you first converted |
397 | /// `self` to a BCP-47 string and then performed a byte comparison. |
398 | /// |
399 | /// This function is case-sensitive and results in a *total order*, so it is appropriate for |
400 | /// binary search. The only argument producing [`Ordering::Equal`](core::cmp::Ordering::Equal) |
401 | /// is `self.as_str().as_bytes()`. |
402 | #[inline] |
403 | pub fn strict_cmp(self, other: &[u8]) -> core::cmp::Ordering { |
404 | self.as_str().as_bytes().cmp(other) |
405 | } |
406 | |
407 | /// Compare with a potentially unnormalized BCP-47 string. |
408 | /// |
409 | /// The return value is equivalent to what would happen if you first parsed the |
410 | /// BCP-47 string and then performed a structural comparison. |
411 | /// |
412 | #[inline] |
413 | pub fn normalizing_eq(self, other: &str) -> bool { |
414 | self.as_str().eq_ignore_ascii_case(other) |
415 | } |
416 | } |
417 | |
418 | impl core::str::FromStr for $name { |
419 | type Err = crate::parser::errors::ParserError; |
420 | |
421 | fn from_str(source: &str) -> Result<Self, Self::Err> { |
422 | Self::try_from_bytes(source.as_bytes()) |
423 | } |
424 | } |
425 | |
426 | impl<'l> From<&'l $name> for &'l str { |
427 | fn from(input: &'l $name) -> Self { |
428 | input.as_str() |
429 | } |
430 | } |
431 | |
432 | impl From<$name> for tinystr::TinyAsciiStr<$len_end> { |
433 | fn from(input: $name) -> Self { |
434 | input.into_tinystr() |
435 | } |
436 | } |
437 | |
438 | impl writeable::Writeable for $name { |
439 | #[inline] |
440 | fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result { |
441 | sink.write_str(self.as_str()) |
442 | } |
443 | #[inline] |
444 | fn writeable_length_hint(&self) -> writeable::LengthHint { |
445 | writeable::LengthHint::exact(self.0.len()) |
446 | } |
447 | #[inline] |
448 | fn write_to_string(&self) -> alloc::borrow::Cow<str> { |
449 | alloc::borrow::Cow::Borrowed(self.0.as_str()) |
450 | } |
451 | } |
452 | |
453 | writeable::impl_display_with_writeable!($name); |
454 | |
455 | #[doc = concat!("A macro allowing for compile-time construction of valid [`" , stringify!($name), "`] subtags." )] |
456 | /// |
457 | /// # Examples |
458 | /// |
459 | /// Parsing errors don't have to be handled at runtime: |
460 | /// ``` |
461 | /// assert_eq!( |
462 | #[doc = concat!(" icu_locid::" , $(stringify!($path), "::" ,)+ stringify!($macro_name), "!(" , stringify!($good_example) ,")," )] |
463 | #[doc = concat!(" " , stringify!($good_example), ".parse::<icu_locid::" , $(stringify!($path), "::" ,)+ stringify!($name), ">().unwrap()" )] |
464 | /// ); |
465 | /// ``` |
466 | /// |
467 | /// Invalid input is a compile failure: |
468 | /// ```compile_fail,E0080 |
469 | #[doc = concat!("icu_locid::" , $(stringify!($path), "::" ,)+ stringify!($macro_name), "!(" , stringify!($bad_example) ,");" )] |
470 | /// ``` |
471 | /// |
472 | #[doc = concat!("[`" , stringify!($name), "`]: crate::" , $(stringify!($path), "::" ,)+ stringify!($name))] |
473 | #[macro_export] |
474 | #[doc(hidden)] |
475 | macro_rules! $legacy_macro_name { |
476 | ($string:literal) => {{ |
477 | use $crate::$($path ::)+ $name; |
478 | const R: $name = |
479 | match $name::try_from_bytes($string.as_bytes()) { |
480 | Ok(r) => r, |
481 | #[allow(clippy::panic)] // const context |
482 | _ => panic!(concat!("Invalid " , $(stringify!($path), "::" ,)+ stringify!($name), ": " , $string)), |
483 | }; |
484 | R |
485 | }}; |
486 | } |
487 | #[doc(inline)] |
488 | pub use $legacy_macro_name as $macro_name; |
489 | |
490 | #[cfg(feature = "databake" )] |
491 | impl databake::Bake for $name { |
492 | fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream { |
493 | env.insert("icu_locid" ); |
494 | let string = self.as_str(); |
495 | databake::quote! { icu_locid::$($path::)+ $macro_name!(#string) } |
496 | } |
497 | } |
498 | |
499 | #[test] |
500 | fn test_construction() { |
501 | let maybe = $name::try_from_bytes($good_example.as_bytes()); |
502 | assert!(maybe.is_ok()); |
503 | assert_eq!(maybe, $name::try_from_raw(maybe.unwrap().into_raw())); |
504 | assert_eq!(maybe.unwrap().as_str(), $good_example); |
505 | $( |
506 | let maybe = $name::try_from_bytes($more_good_examples.as_bytes()); |
507 | assert!(maybe.is_ok()); |
508 | assert_eq!(maybe, $name::try_from_raw(maybe.unwrap().into_raw())); |
509 | assert_eq!(maybe.unwrap().as_str(), $more_good_examples); |
510 | )* |
511 | assert!($name::try_from_bytes($bad_example.as_bytes()).is_err()); |
512 | $( |
513 | assert!($name::try_from_bytes($more_bad_examples.as_bytes()).is_err()); |
514 | )* |
515 | } |
516 | |
517 | #[test] |
518 | fn test_writeable() { |
519 | writeable::assert_writeable_eq!(&$good_example.parse::<$name>().unwrap(), $good_example); |
520 | $( |
521 | writeable::assert_writeable_eq!($more_good_examples.parse::<$name>().unwrap(), $more_good_examples); |
522 | )* |
523 | } |
524 | |
525 | #[cfg(feature = "serde" )] |
526 | impl<'de> serde::Deserialize<'de> for $name { |
527 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
528 | where |
529 | D: serde::de::Deserializer<'de>, |
530 | { |
531 | struct Visitor; |
532 | |
533 | impl<'de> serde::de::Visitor<'de> for Visitor { |
534 | type Value = $name; |
535 | |
536 | fn expecting( |
537 | &self, |
538 | formatter: &mut core::fmt::Formatter<'_>, |
539 | ) -> core::fmt::Result { |
540 | write!(formatter, "a valid BCP-47 {}" , stringify!($name)) |
541 | } |
542 | |
543 | fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> { |
544 | s.parse().map_err(serde::de::Error::custom) |
545 | } |
546 | } |
547 | |
548 | if deserializer.is_human_readable() { |
549 | deserializer.deserialize_string(Visitor) |
550 | } else { |
551 | Self::try_from_raw(serde::de::Deserialize::deserialize(deserializer)?) |
552 | .map_err(serde::de::Error::custom) |
553 | } |
554 | } |
555 | } |
556 | |
557 | // Safety checklist for ULE: |
558 | // |
559 | // 1. Must not include any uninitialized or padding bytes (true since transparent over a ULE). |
560 | // 2. Must have an alignment of 1 byte (true since transparent over a ULE). |
561 | // 3. ULE::validate_byte_slice() checks that the given byte slice represents a valid slice. |
562 | // 4. ULE::validate_byte_slice() checks that the given byte slice has a valid length. |
563 | // 5. All other methods must be left with their default impl. |
564 | // 6. Byte equality is semantic equality. |
565 | #[cfg(feature = "zerovec" )] |
566 | unsafe impl zerovec::ule::ULE for $name { |
567 | fn validate_byte_slice(bytes: &[u8]) -> Result<(), zerovec::ZeroVecError> { |
568 | let it = bytes.chunks_exact(core::mem::size_of::<Self>()); |
569 | if !it.remainder().is_empty() { |
570 | return Err(zerovec::ZeroVecError::length::<Self>(bytes.len())); |
571 | } |
572 | for v in it { |
573 | // The following can be removed once `array_chunks` is stabilized. |
574 | let mut a = [0; core::mem::size_of::<Self>()]; |
575 | a.copy_from_slice(v); |
576 | if Self::try_from_raw(a).is_err() { |
577 | return Err(zerovec::ZeroVecError::parse::<Self>()); |
578 | } |
579 | } |
580 | Ok(()) |
581 | } |
582 | } |
583 | |
584 | #[cfg(feature = "zerovec" )] |
585 | impl zerovec::ule::AsULE for $name { |
586 | type ULE = Self; |
587 | fn to_unaligned(self) -> Self::ULE { |
588 | self |
589 | } |
590 | fn from_unaligned(unaligned: Self::ULE) -> Self { |
591 | unaligned |
592 | } |
593 | } |
594 | |
595 | #[cfg(feature = "zerovec" )] |
596 | impl<'a> zerovec::maps::ZeroMapKV<'a> for $name { |
597 | type Container = zerovec::ZeroVec<'a, $name>; |
598 | type Slice = zerovec::ZeroSlice<$name>; |
599 | type GetType = $name; |
600 | type OwnedType = $name; |
601 | } |
602 | }; |
603 | } |
604 | |
605 | macro_rules! impl_writeable_for_each_subtag_str_no_test { |
606 | ($type:tt $(, $self:ident, $borrow_cond:expr => $borrow:expr)?) => { |
607 | impl writeable::Writeable for $type { |
608 | fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result { |
609 | let mut initial = true; |
610 | self.for_each_subtag_str(&mut |subtag| { |
611 | if initial { |
612 | initial = false; |
613 | } else { |
614 | sink.write_char('-' )?; |
615 | } |
616 | sink.write_str(subtag) |
617 | }) |
618 | } |
619 | |
620 | #[inline] |
621 | fn writeable_length_hint(&self) -> writeable::LengthHint { |
622 | let mut result = writeable::LengthHint::exact(0); |
623 | let mut initial = true; |
624 | self.for_each_subtag_str::<core::convert::Infallible, _>(&mut |subtag| { |
625 | if initial { |
626 | initial = false; |
627 | } else { |
628 | result += 1; |
629 | } |
630 | result += subtag.len(); |
631 | Ok(()) |
632 | }) |
633 | .expect("infallible" ); |
634 | result |
635 | } |
636 | |
637 | $( |
638 | fn write_to_string(&self) -> alloc::borrow::Cow<str> { |
639 | #[allow(clippy::unwrap_used)] // impl_writeable_for_subtag_list's $borrow uses unwrap |
640 | let $self = self; |
641 | if $borrow_cond { |
642 | $borrow |
643 | } else { |
644 | let mut output = alloc::string::String::with_capacity(self.writeable_length_hint().capacity()); |
645 | let _ = self.write_to(&mut output); |
646 | alloc::borrow::Cow::Owned(output) |
647 | } |
648 | } |
649 | )? |
650 | } |
651 | |
652 | writeable::impl_display_with_writeable!($type); |
653 | }; |
654 | } |
655 | |
656 | macro_rules! impl_writeable_for_subtag_list { |
657 | ($type:tt, $sample1:literal, $sample2:literal) => { |
658 | impl_writeable_for_each_subtag_str_no_test!($type, selff, selff.0.len() == 1 => alloc::borrow::Cow::Borrowed(selff.0.get(0).unwrap().as_str())); |
659 | |
660 | #[test] |
661 | fn test_writeable() { |
662 | writeable::assert_writeable_eq!(&$type::default(), "" ); |
663 | writeable::assert_writeable_eq!( |
664 | &$type::from_short_slice_unchecked(alloc::vec![$sample1.parse().unwrap()].into()), |
665 | $sample1, |
666 | ); |
667 | writeable::assert_writeable_eq!( |
668 | &$type::from_short_slice_unchecked(vec![ |
669 | $sample1.parse().unwrap(), |
670 | $sample2.parse().unwrap() |
671 | ].into()), |
672 | core::concat!($sample1, "-" , $sample2), |
673 | ); |
674 | } |
675 | }; |
676 | } |
677 | |
678 | macro_rules! impl_writeable_for_key_value { |
679 | ($type:tt, $key1:literal, $value1:literal, $key2:literal, $expected2:literal) => { |
680 | impl_writeable_for_each_subtag_str_no_test!($type); |
681 | |
682 | #[test] |
683 | fn test_writeable() { |
684 | writeable::assert_writeable_eq!(&$type::default(), "" ); |
685 | writeable::assert_writeable_eq!( |
686 | &$type::from_tuple_vec(vec![($key1.parse().unwrap(), $value1.parse().unwrap())]), |
687 | core::concat!($key1, "-" , $value1), |
688 | ); |
689 | writeable::assert_writeable_eq!( |
690 | &$type::from_tuple_vec(vec![ |
691 | ($key1.parse().unwrap(), $value1.parse().unwrap()), |
692 | ($key2.parse().unwrap(), "true" .parse().unwrap()) |
693 | ]), |
694 | core::concat!($key1, "-" , $value1, "-" , $expected2), |
695 | ); |
696 | } |
697 | }; |
698 | } |
699 | |