|
15 | 15 | //!
|
16 | 16 | //! Read more about data providers: [`icu_provider`]
|
17 | 17 |
|
18 |
| -use crate::rules::runtime::ast::Rule; |
| 18 | +use core::marker::PhantomData; |
19 | 19 | use icu_provider::prelude::*;
|
20 | 20 | use icu_provider::DynamicDataMarker;
|
| 21 | +use zerovec::ule::vartuple::TinyVarVar; |
| 22 | +use zerovec::ule::vartuple::TinyVarVarULE; |
| 23 | +use zerovec::ule::vartuple::VarTupleULE; |
| 24 | +use zerovec::ule::UleError; |
| 25 | +use zerovec::ule::VarULE; |
| 26 | +use zerovec::ule::ULE; |
| 27 | +use zerovec::VarZeroSlice; |
| 28 | +use crate::rules::runtime::ast::Rule; |
21 | 29 |
|
22 | 30 | #[cfg(feature = "compiled_data")]
|
23 | 31 | #[derive(Debug)]
|
@@ -327,7 +335,7 @@ use alloc::borrow::Cow;
|
327 | 335 | use zerovec::ule::AsULE;
|
328 | 336 | use zerovec::VarZeroVec;
|
329 | 337 |
|
330 |
| -#[derive(Debug, Clone, Copy)] |
| 338 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
331 | 339 | #[zerovec::make_ule(PluralCategoryV1ULE)]
|
332 | 340 | #[repr(u8)]
|
333 | 341 | #[cfg_attr(feature = "datagen", derive(serde::Serialize))]
|
@@ -475,12 +483,190 @@ impl<'a, 'b> From<PluralElements<'b, str>> for PluralElementsV1<'a> {
|
475 | 483 | }
|
476 | 484 | }
|
477 | 485 |
|
478 |
| -struct PluralElementsSingletonVarULE<V> {} |
479 |
| - |
480 |
| -struct PluralElementsMapVarULE {} |
481 |
| - |
482 | 486 | #[test]
|
483 | 487 | fn plural_elements_niche() {
|
484 | 488 | assert_eq!(core::mem::size_of::<PluralElementsV1>(), 48);
|
485 | 489 | assert_eq!(core::mem::size_of::<Option<PluralElementsV1>>(), 48);
|
486 | 490 | }
|
| 491 | + |
| 492 | +/// Four bits of metadata that are stored and retrieved with the plural elements. |
| 493 | +#[derive(Debug, Copy, Clone)] |
| 494 | +#[repr(transparent)] |
| 495 | +pub struct FourBitMetadata(u8); |
| 496 | + |
| 497 | +/// Representation: `ppppmmmm` |
| 498 | +/// - `pppp` = plural category (including explicit value) |
| 499 | +/// - `mmmm` = [`FourBitMetadata`] |
| 500 | +#[derive(Debug, Copy, Clone)] |
| 501 | +#[repr(transparent)] |
| 502 | +struct PluralCategoryAndMetadata( |
| 503 | + // Invariant: representation is as stated in the struct docs |
| 504 | + u8, |
| 505 | +); |
| 506 | + |
| 507 | +impl PluralCategoryAndMetadata { |
| 508 | + fn get(self) -> (PluralElementsKeysV1, FourBitMetadata) { |
| 509 | + let plural_category_byte = (self.0 & 0xF0) >> 4; |
| 510 | + // Safety: by invariant |
| 511 | + let plural_category = |
| 512 | + unsafe { PluralElementsKeysV1::new_from_u8(plural_category_byte).unwrap_unchecked() }; |
| 513 | + let metadata = FourBitMetadata(self.0 & 0x0F); |
| 514 | + (plural_category, metadata) |
| 515 | + } |
| 516 | +} |
| 517 | + |
| 518 | +unsafe impl ULE for PluralCategoryAndMetadata { |
| 519 | + fn validate_byte_slice(bytes: &[u8]) -> Result<(), zerovec::ule::UleError> { |
| 520 | + bytes |
| 521 | + .iter() |
| 522 | + .all(|byte| { |
| 523 | + let plural_category_byte = (byte & 0xF0) >> 4; |
| 524 | + PluralElementsKeysV1::new_from_u8(plural_category_byte).is_some() |
| 525 | + }) |
| 526 | + .then_some(()) |
| 527 | + .ok_or_else(|| UleError::parse::<Self>()) |
| 528 | + } |
| 529 | +} |
| 530 | + |
| 531 | +impl AsULE for PluralCategoryAndMetadata { |
| 532 | + type ULE = Self; |
| 533 | + #[inline] |
| 534 | + fn to_unaligned(self) -> Self::ULE { |
| 535 | + self |
| 536 | + } |
| 537 | + #[inline] |
| 538 | + fn from_unaligned(unaligned: Self::ULE) -> Self { |
| 539 | + unaligned |
| 540 | + } |
| 541 | +} |
| 542 | + |
| 543 | +type PluralElementsSingletonVarULE<V> = V; |
| 544 | + |
| 545 | +type PluralElementsTupleSliceVarULE<V> = VarZeroSlice<VarTupleULE<PluralCategoryAndMetadata, V>>; |
| 546 | + |
| 547 | +type PluralElementsDefaultMapVarULE<V> = TinyVarVarULE<V, PluralElementsTupleSliceVarULE<V>>; |
| 548 | + |
| 549 | +fn get_special<V: VarULE + ?Sized>( |
| 550 | + data: &PluralElementsTupleSliceVarULE<V>, |
| 551 | + key: PluralElementsKeysV1, |
| 552 | +) -> Option<(&V, FourBitMetadata)> { |
| 553 | + data.iter() |
| 554 | + .filter_map(|ule| { |
| 555 | + let (current_key, metadata) = ule.sized.get(); |
| 556 | + (current_key == key).then_some((&ule.variable, metadata)) |
| 557 | + }) |
| 558 | + .next() |
| 559 | +} |
| 560 | + |
| 561 | +/// A bitpacked DST for [`PluralElements`]. |
| 562 | +/// |
| 563 | +/// Can be put in a [`Cow`] or a [`VarZeroVec`]. |
| 564 | +#[derive(Debug)] |
| 565 | +#[repr(transparent)] |
| 566 | +pub struct PluralElementsPackedULE<V: VarULE + ?Sized> { |
| 567 | + _v: PhantomData<V>, |
| 568 | + /// Invariant Representation: |
| 569 | + /// |
| 570 | + /// First byte: `d...mmmm` |
| 571 | + /// - `d` = 0 if singleton, 1 if a map |
| 572 | + /// - `...` = padding, should be 0 |
| 573 | + /// - `mmmm` = [`FourBitMetadata`] for the default value |
| 574 | + /// |
| 575 | + /// Remainder: either [`PluralElementsSingletonVarULE`] or [`PluralElementsDefaultMapVarULE`] |
| 576 | + /// based on the discriminant `d` |
| 577 | + bytes: [u8], |
| 578 | +} |
| 579 | + |
| 580 | +impl<V: VarULE + ?Sized> ToOwned for PluralElementsPackedULE<V> { |
| 581 | + type Owned = Box<PluralElementsPackedULE<V>>; |
| 582 | + fn to_owned(&self) -> Self::Owned { |
| 583 | + self.to_boxed() |
| 584 | + } |
| 585 | +} |
| 586 | + |
| 587 | +unsafe impl<V> VarULE for PluralElementsPackedULE<V> |
| 588 | +where |
| 589 | + V: VarULE + ?Sized, |
| 590 | +{ |
| 591 | + fn validate_byte_slice(bytes: &[u8]) -> Result<(), zerovec::ule::UleError> { |
| 592 | + let (lead_byte, remainder) = bytes |
| 593 | + .split_first() |
| 594 | + .ok_or_else(|| UleError::length::<Self>(bytes.len()))?; |
| 595 | + if lead_byte & 0x8F != 0 { |
| 596 | + return Err(UleError::parse::<Self>()); |
| 597 | + } |
| 598 | + if lead_byte & 0x80 == 0 { |
| 599 | + PluralElementsSingletonVarULE::<V>::validate_byte_slice(remainder) |
| 600 | + } else { |
| 601 | + PluralElementsDefaultMapVarULE::<V>::validate_byte_slice(remainder) |
| 602 | + } |
| 603 | + } |
| 604 | + |
| 605 | + unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { |
| 606 | + // Safety: the bytes are valid by trait invariant, and we are transparent over bytes |
| 607 | + core::mem::transmute(bytes) |
| 608 | + } |
| 609 | +} |
| 610 | + |
| 611 | +impl<V> PluralElementsPackedULE<V> |
| 612 | +where |
| 613 | + V: VarULE + ?Sized, |
| 614 | +{ |
| 615 | + /// Unpacks this structure into the default value and the optional list of specials. |
| 616 | + fn as_parts( |
| 617 | + &self, |
| 618 | + ) -> ( |
| 619 | + (&V, FourBitMetadata), |
| 620 | + Option<&PluralElementsTupleSliceVarULE<V>>, |
| 621 | + ) { |
| 622 | + // Safety: the bytes are valid by invariant |
| 623 | + let (lead_byte, remainder) = unsafe { self.bytes.split_first().unwrap_unchecked() }; |
| 624 | + let metadata = FourBitMetadata(lead_byte & 0x0F); |
| 625 | + if lead_byte & 0x80 == 0 { |
| 626 | + // Safety: the bytes are valid by invariant |
| 627 | + let inner = |
| 628 | + unsafe { PluralElementsSingletonVarULE::<V>::from_byte_slice_unchecked(remainder) }; |
| 629 | + ((inner, metadata), None) |
| 630 | + } else { |
| 631 | + // Safety: the bytes are valid by invariant |
| 632 | + let inner = unsafe { |
| 633 | + PluralElementsDefaultMapVarULE::<V>::from_byte_slice_unchecked(remainder) |
| 634 | + }; |
| 635 | + let (default, tuple_slice) = inner.get(); |
| 636 | + ((default, metadata), Some(tuple_slice)) |
| 637 | + } |
| 638 | + } |
| 639 | + |
| 640 | + /// Returns the value for the given [`PluralOperands`] and [`PluralRules`]. |
| 641 | + pub fn get<'a>(&'a self, op: PluralOperands, rules: &PluralRules) -> (&'a V, FourBitMetadata) { |
| 642 | + let (default, specials) = self.as_parts(); |
| 643 | + |
| 644 | + let category = rules.category_for(op); |
| 645 | + |
| 646 | + match specials { |
| 647 | + Some(specials) => { |
| 648 | + if op.is_exactly_zero() { |
| 649 | + if let Some(value) = get_special(specials, PluralElementsKeysV1::ExplicitZero) { |
| 650 | + return value; |
| 651 | + } |
| 652 | + } |
| 653 | + if op.is_exactly_one() { |
| 654 | + if let Some(value) = get_special(specials, PluralElementsKeysV1::ExplicitOne) { |
| 655 | + return value; |
| 656 | + } |
| 657 | + } |
| 658 | + match category { |
| 659 | + PluralCategory::Zero => Some(PluralElementsKeysV1::Zero), |
| 660 | + PluralCategory::One => Some(PluralElementsKeysV1::One), |
| 661 | + PluralCategory::Two => Some(PluralElementsKeysV1::Two), |
| 662 | + PluralCategory::Few => Some(PluralElementsKeysV1::Few), |
| 663 | + PluralCategory::Many => Some(PluralElementsKeysV1::Many), |
| 664 | + PluralCategory::Other => None, |
| 665 | + } |
| 666 | + .and_then(|key| get_special(specials, key)) |
| 667 | + } |
| 668 | + None => None, |
| 669 | + } |
| 670 | + .unwrap_or(default) |
| 671 | + } |
| 672 | +} |
0 commit comments