1use crate::fallback::{LocaleFallbackConfig, LocaleFallbackPriority};
6use crate::{DataError, DataErrorKind, DataLocale, DataProvider, DataProviderWithMarker};
7use core::fmt;
8use core::marker::PhantomData;
9use icu_locale_core::preferences::LocalePreferences;
10use yoke::Yokeable;
11use zerovec::ule::*;
12
13pub trait DynamicDataMarker: 'static {
53 type DataStruct: for<'a> Yokeable<'a>;
56}
57
58pub trait DataMarker: DynamicDataMarker {
76 const INFO: DataMarkerInfo;
78}
79
80pub trait DataMarkerExt: DataMarker + Sized {
82 fn bind<P>(provider: P) -> DataProviderWithMarker<Self, P>
84 where
85 P: DataProvider<Self>;
86 fn make_locale(locale: LocalePreferences) -> DataLocale;
88}
89
90impl<M: DataMarker + Sized> DataMarkerExt for M {
91 fn bind<P>(provider: P) -> DataProviderWithMarker<Self, P>
92 where
93 P: DataProvider<Self>,
94 {
95 DataProviderWithMarker::new(provider)
96 }
97
98 fn make_locale(locale: LocalePreferences) -> DataLocale {
99 M::INFO.make_locale(locale)
100 }
101}
102
103#[derive(#[automatically_derived]
impl<Y: ::core::fmt::Debug> ::core::fmt::Debug for NeverMarker<Y> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "NeverMarker",
&&self.0)
}
}Debug, #[automatically_derived]
impl<Y: ::core::marker::Copy> ::core::marker::Copy for NeverMarker<Y> { }Copy, #[automatically_derived]
impl<Y: ::core::clone::Clone> ::core::clone::Clone for NeverMarker<Y> {
#[inline]
fn clone(&self) -> NeverMarker<Y> {
NeverMarker(::core::clone::Clone::clone(&self.0))
}
}Clone)]
139pub struct NeverMarker<Y>(PhantomData<Y>);
140
141impl<Y> DynamicDataMarker for NeverMarker<Y>
142where
143 for<'a> Y: Yokeable<'a>,
144{
145 type DataStruct = Y;
146}
147
148impl<Y> DataMarker for NeverMarker<Y>
149where
150 for<'a> Y: Yokeable<'a>,
151{
152 const INFO: DataMarkerInfo = DataMarkerInfo::from_id(DataMarkerId {
153 #[cfg(any(feature = "export", debug_assertions))]
154 debug: "NeverMarker",
155 hash: *b"nevermar",
156 });
157}
158
159#[doc(hidden)] #[macro_export]
193macro_rules! __impl_data_provider_never_marker {
194 ($ty:path) => {
195 impl<Y> $crate::DataProvider<$crate::marker::NeverMarker<Y>> for $ty
196 where
197 for<'a> Y: $crate::prelude::yoke::Yokeable<'a>,
198 {
199 fn load(
200 &self,
201 req: $crate::DataRequest,
202 ) -> Result<$crate::DataResponse<$crate::marker::NeverMarker<Y>>, $crate::DataError>
203 {
204 Err($crate::DataErrorKind::MarkerNotFound.with_req(
205 <$crate::marker::NeverMarker<Y> as $crate::DataMarker>::INFO,
206 req,
207 ))
208 }
209 }
210 };
211}
212#[doc(inline)]
213pub use __impl_data_provider_never_marker as impl_data_provider_never_marker;
214
215#[derive(#[automatically_derived]
impl ::core::fmt::Debug for DataMarkerIdHash {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"DataMarkerIdHash", &&self.0)
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for DataMarkerIdHash {
#[inline]
fn eq(&self, other: &DataMarkerIdHash) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for DataMarkerIdHash {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<[u8; 4]>;
}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for DataMarkerIdHash {
#[inline]
fn partial_cmp(&self, other: &DataMarkerIdHash)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
}
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for DataMarkerIdHash {
#[inline]
fn cmp(&self, other: &DataMarkerIdHash) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}Ord, #[automatically_derived]
impl ::core::marker::Copy for DataMarkerIdHash { }Copy, #[automatically_derived]
impl ::core::clone::Clone for DataMarkerIdHash {
#[inline]
fn clone(&self) -> DataMarkerIdHash {
let _: ::core::clone::AssertParamIsClone<[u8; 4]>;
*self
}
}Clone, #[automatically_derived]
impl ::core::hash::Hash for DataMarkerIdHash {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, unsafe impl zerovec::ule::ULE for DataMarkerIdHash {
#[inline]
fn validate_bytes(bytes: &[u8]) -> Result<(), zerovec::ule::UleError> {
const SIZE: usize = ::core::mem::size_of::<DataMarkerIdHash>();
#[expect(clippy :: modulo_one)]
if bytes.len() % SIZE != 0 {
return Err(zerovec::ule::UleError::length::<Self>(bytes.len()));
}
#[expect(clippy :: indexing_slicing)]
for chunk in bytes.chunks_exact(SIZE) {
const ZERO: usize = 0;
;
const SIZE_0: usize = ::core::mem::size_of::<[u8; 4]>();
const OFFSET_0: usize = ZERO + SIZE_0;
if let Some(bytes) = bytes.get(ZERO..ZERO + SIZE_0) {
<[u8; 4] as zerovec::ule::ULE>::validate_bytes(bytes)?;
} else { return Err(zerovec::ule::UleError::parse::<Self>()); };
if true {
match (&OFFSET_0, &SIZE) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};
}
Ok(())
}
}ULE)]
219#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
220#[repr(transparent)]
221pub struct DataMarkerIdHash([u8; 4]);
222
223impl DataMarkerIdHash {
224 pub const LEADING_TAG: &[u8] = b"tdmh";
226
227 pub const fn to_bytes(self) -> [u8; 4] {
229 self.0
230 }
231}
232
233#[expect(clippy::indexing_slicing)]
250const fn fxhash_32(bytes: &[u8]) -> u32 {
251 #[inline]
265 const fn hash_word_32(mut hash: u32, word: u32) -> u32 {
266 const ROTATE: u32 = 5;
267 const SEED32: u32 = 0x9e_37_79_b9;
268 hash = hash.rotate_left(ROTATE);
269 hash ^= word;
270 hash = hash.wrapping_mul(SEED32);
271 hash
272 }
273
274 let mut cursor = 0;
275 let end = bytes.len();
276 let mut hash = 0;
277
278 while end - cursor >= 4 {
279 let word = u32::from_le_bytes([
280 bytes[cursor],
281 bytes[cursor + 1],
282 bytes[cursor + 2],
283 bytes[cursor + 3],
284 ]);
285 hash = hash_word_32(hash, word);
286 cursor += 4;
287 }
288
289 if end - cursor >= 2 {
290 let word = u16::from_le_bytes([bytes[cursor], bytes[cursor + 1]]);
291 hash = hash_word_32(hash, word as u32);
292 cursor += 2;
293 }
294
295 if end - cursor >= 1 {
296 hash = hash_word_32(hash, bytes[cursor] as u32);
297 }
298
299 hash
300}
301
302#[cfg(feature = "alloc")]
303impl<'a> zerovec::maps::ZeroMapKV<'a> for DataMarkerIdHash {
304 type Container = zerovec::ZeroVec<'a, DataMarkerIdHash>;
305 type Slice = zerovec::ZeroSlice<DataMarkerIdHash>;
306 type GetType = <DataMarkerIdHash as AsULE>::ULE;
307 type OwnedType = DataMarkerIdHash;
308}
309
310impl AsULE for DataMarkerIdHash {
311 type ULE = Self;
312 #[inline]
313 fn to_unaligned(self) -> Self::ULE {
314 self
315 }
316 #[inline]
317 fn from_unaligned(unaligned: Self::ULE) -> Self {
318 unaligned
319 }
320}
321
322unsafe impl EqULE for DataMarkerIdHash {}
324
325#[derive(#[automatically_derived]
impl ::core::fmt::Debug for DataMarkerId {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "DataMarkerId",
"debug", &self.debug, "hash", &&self.hash)
}
}Debug, #[automatically_derived]
impl ::core::marker::Copy for DataMarkerId { }Copy, #[automatically_derived]
impl ::core::clone::Clone for DataMarkerId {
#[inline]
fn clone(&self) -> DataMarkerId {
let _: ::core::clone::AssertParamIsClone<&'static str>;
let _: ::core::clone::AssertParamIsClone<[u8; 8]>;
*self
}
}Clone, #[automatically_derived]
impl ::core::cmp::Eq for DataMarkerId {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<&'static str>;
let _: ::core::cmp::AssertParamIsEq<[u8; 8]>;
}
}Eq)]
330pub struct DataMarkerId {
331 #[cfg(any(feature = "export", debug_assertions))]
332 debug: &'static str,
333 hash: [u8; 8],
334}
335
336impl PartialEq for DataMarkerId {
337 #[inline]
338 fn eq(&self, other: &Self) -> bool {
339 self.hash == other.hash
340 }
341}
342
343impl Ord for DataMarkerId {
344 #[inline]
345 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
346 self.hash.cmp(&other.hash)
347 }
348}
349
350impl PartialOrd for DataMarkerId {
351 #[inline]
352 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
353 Some(self.cmp(other))
354 }
355}
356
357impl core::hash::Hash for DataMarkerId {
358 #[inline]
359 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
360 self.hash.hash(state)
361 }
362}
363
364impl DataMarkerId {
365 #[doc(hidden)]
366 pub const fn from_name(name: &'static str) -> Result<Self, (&'static str, usize)> {
370 #![allow(clippy::indexing_slicing)]
371 if !name.as_bytes()[name.len() - 1].is_ascii_digit() {
372 return Err(("[0-9]", name.len()));
373 }
374 let mut i = name.len() - 1;
375 while name.as_bytes()[i - 1].is_ascii_digit() {
376 i -= 1;
377 }
378 if name.as_bytes()[i - 1] != b'V' {
379 return Err(("V", i));
380 }
381
382 let magic = DataMarkerIdHash::LEADING_TAG;
383 let hash = fxhash_32(name.as_bytes()).to_le_bytes();
384
385 Ok(Self {
386 #[cfg(any(feature = "export", debug_assertions))]
387 debug: name,
388 hash: [
389 magic[0], magic[1], magic[2], magic[3], hash[0], hash[1], hash[2], hash[3],
390 ],
391 })
392 }
393
394 #[inline]
408 pub const fn hashed(self) -> DataMarkerIdHash {
409 let [.., h1, h2, h3, h4] = self.hash;
410 DataMarkerIdHash([h1, h2, h3, h4])
411 }
412
413 #[cfg(feature = "export")]
417 pub const fn name(self) -> &'static str {
418 self.debug
419 }
420}
421
422#[derive(#[automatically_derived]
impl ::core::marker::Copy for DataMarkerInfo { }Copy, #[automatically_derived]
impl ::core::clone::Clone for DataMarkerInfo {
#[inline]
fn clone(&self) -> DataMarkerInfo {
let _: ::core::clone::AssertParamIsClone<DataMarkerId>;
let _: ::core::clone::AssertParamIsClone<bool>;
let _: ::core::clone::AssertParamIsClone<LocaleFallbackConfig>;
*self
}
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for DataMarkerInfo {
#[inline]
fn eq(&self, other: &DataMarkerInfo) -> bool {
self.is_singleton == other.is_singleton &&
self.has_checksum == other.has_checksum &&
self.id == other.id &&
self.fallback_config == other.fallback_config
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for DataMarkerInfo {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<DataMarkerId>;
let _: ::core::cmp::AssertParamIsEq<bool>;
let _: ::core::cmp::AssertParamIsEq<LocaleFallbackConfig>;
}
}Eq)]
429#[non_exhaustive]
430pub struct DataMarkerInfo {
431 pub id: DataMarkerId,
433 pub is_singleton: bool,
435 pub has_checksum: bool,
437 pub fallback_config: LocaleFallbackConfig,
439 #[cfg(feature = "export")]
442 pub attributes_domain: &'static str,
443 #[cfg(feature = "export")]
445 pub expose_baked_consts: bool,
446}
447
448impl PartialOrd for DataMarkerInfo {
449 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
450 Some(self.cmp(other))
451 }
452}
453
454impl Ord for DataMarkerInfo {
455 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
456 self.id.cmp(&other.id)
457 }
458}
459
460impl core::hash::Hash for DataMarkerInfo {
461 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
462 self.id.hash(state)
463 }
464}
465
466impl DataMarkerInfo {
467 pub const fn from_id(id: DataMarkerId) -> Self {
469 Self {
470 id,
471 fallback_config: LocaleFallbackConfig::default(),
472 is_singleton: false,
473 has_checksum: false,
474 #[cfg(feature = "export")]
475 attributes_domain: "",
476 #[cfg(feature = "export")]
477 expose_baked_consts: false,
478 }
479 }
480
481 pub fn match_marker(self, marker: Self) -> Result<(), DataError> {
518 if self == marker {
519 Ok(())
520 } else {
521 Err(DataErrorKind::MarkerNotFound.with_marker(marker))
522 }
523 }
524
525 pub fn make_locale(self, locale: LocalePreferences) -> DataLocale {
527 if self.fallback_config.priority == LocaleFallbackPriority::Region {
528 locale.to_data_locale_region_priority()
529 } else {
530 locale.to_data_locale_language_priority()
531 }
532 }
533}
534
535#[macro_export] macro_rules! data_marker {
552 ($(#[$doc:meta])* $name:ident, $($debug:literal,)? $struct:ty $(, $(#[$meta:meta])* $info_field:ident = $info_val:expr)* $(,)?) => {
553 $(#[$doc])*
554 #[non_exhaustive]
555 pub struct $name;
556 impl $crate::DynamicDataMarker for $name {
557 type DataStruct = $struct;
558 }
559 impl $crate::DataMarker for $name {
560 const INFO: $crate::DataMarkerInfo = {
561 $(
562 #[doc = concat!("let ident = \"", stringify!($name), "\";")]
564 #[doc = concat!("let debug = \"", $debug, "\";")]
565 #[allow(dead_code)]
577 struct DebugTest;
578 )?
579 #[allow(unused_mut)]
580 let mut info = const { $crate::DataMarkerInfo::from_id(
582 match $crate::marker::DataMarkerId::from_name(stringify!($name)) {
583 Ok(path) => path,
584 Err(_) => panic!(concat!("Invalid marker name: ", stringify!($name))),
585 })};
586 $(
587 $(#[$meta])*
588 {info.$info_field = $info_val;}
589 )*
590 info
591 };
592 }
593 }
594}
595
596impl fmt::Debug for DataMarkerInfo {
597 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
598 #[cfg(any(feature = "export", debug_assertions))]
599 return f.write_str(self.id.debug);
600 #[cfg(not(any(feature = "export", debug_assertions)))]
601 return write!(f, "{:?}", self.id);
602 }
603}
604
605#[derive(#[automatically_derived]
impl<DataStruct: ::core::clone::Clone + for<'a> Yokeable<'a>>
::core::clone::Clone for ErasedMarker<DataStruct> {
#[inline]
fn clone(&self) -> ErasedMarker<DataStruct> {
ErasedMarker(::core::clone::Clone::clone(&self.0))
}
}Clone, #[automatically_derived]
impl<DataStruct: ::core::marker::Copy + for<'a> Yokeable<'a>>
::core::marker::Copy for ErasedMarker<DataStruct> {
}Copy, #[automatically_derived]
impl<DataStruct: ::core::cmp::PartialEq + for<'a> Yokeable<'a>>
::core::cmp::PartialEq for ErasedMarker<DataStruct> {
#[inline]
fn eq(&self, other: &ErasedMarker<DataStruct>) -> bool {
self.0 == other.0
}
}PartialEq, #[automatically_derived]
impl<DataStruct: ::core::cmp::Eq + for<'a> Yokeable<'a>> ::core::cmp::Eq for
ErasedMarker<DataStruct> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<PhantomData<DataStruct>>;
}
}Eq, #[automatically_derived]
impl<DataStruct: ::core::hash::Hash + for<'a> Yokeable<'a>> ::core::hash::Hash
for ErasedMarker<DataStruct> {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, #[automatically_derived]
impl<DataStruct: ::core::fmt::Debug + for<'a> Yokeable<'a>> ::core::fmt::Debug
for ErasedMarker<DataStruct> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "ErasedMarker",
&&self.0)
}
}Debug)]
607pub struct ErasedMarker<DataStruct: for<'a> Yokeable<'a>>(PhantomData<DataStruct>);
608impl<DataStruct: for<'a> Yokeable<'a>> DynamicDataMarker for ErasedMarker<DataStruct> {
609 type DataStruct = DataStruct;
610}
611
612#[test]
613fn test_marker_syntax() {
614 DataMarkerId::from_name("HelloWorldV1").unwrap();
616 DataMarkerId::from_name("HelloWorldFooV1").unwrap();
617 DataMarkerId::from_name("HelloWorldV999").unwrap();
618 DataMarkerId::from_name("Hello485FooV1").unwrap();
619
620 assert_eq!(
622 DataMarkerId::from_name("HelloWorld"),
623 Err(("[0-9]", "HelloWorld".len()))
624 );
625
626 assert_eq!(
627 DataMarkerId::from_name("HelloWorldV"),
628 Err(("[0-9]", "HelloWorldV".len()))
629 );
630 assert_eq!(
631 DataMarkerId::from_name("HelloWorldVFoo"),
632 Err(("[0-9]", "HelloWorldVFoo".len()))
633 );
634 assert_eq!(
635 DataMarkerId::from_name("HelloWorldV1Foo"),
636 Err(("[0-9]", "HelloWorldV1Foo".len()))
637 );
638}
639
640#[test]
641fn test_id_debug() {
642 assert_eq!(DataMarkerId::from_name("BarV1").unwrap().debug, "BarV1");
643}
644
645#[test]
646fn test_hash_word_32() {
647 assert_eq!(0, fxhash_32(b""));
648 assert_eq!(0xF3051F19, fxhash_32(b"a"));
649 assert_eq!(0x2F9DF119, fxhash_32(b"ab"));
650 assert_eq!(0xCB1D9396, fxhash_32(b"abc"));
651 assert_eq!(0x8628F119, fxhash_32(b"abcd"));
652 assert_eq!(0xBEBDB56D, fxhash_32(b"abcde"));
653 assert_eq!(0x1CE8476D, fxhash_32(b"abcdef"));
654 assert_eq!(0xC0F176A4, fxhash_32(b"abcdefg"));
655 assert_eq!(0x09AB476D, fxhash_32(b"abcdefgh"));
656 assert_eq!(0xB72F5D88, fxhash_32(b"abcdefghi"));
657}
658
659#[test]
660fn test_id_hash() {
661 assert_eq!(
662 DataMarkerId::from_name("BarV1").unwrap().hashed(),
663 DataMarkerIdHash([212, 77, 158, 241]),
664 );
665}