• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 super::*;
6 use crate::varzerovec::lengthless::VarZeroLengthlessSlice;
7 use crate::vecs::VarZeroVecFormat;
8 use core::{fmt, mem};
9 
10 /// This type is used by the custom derive to represent multiple [`VarULE`]
11 /// fields packed into a single end-of-struct field. It is not recommended
12 /// to use this type directly, use [`Tuple2VarULE`](crate::ule::tuplevar::Tuple2VarULE) etc instead.
13 ///
14 /// Logically, consider it to be `(, , , ..)`
15 /// where `` etc are potentially different [`VarULE`] types.
16 ///
17 /// Internally, it is represented by a VarZeroSlice without the length part.
18 #[derive(PartialEq, Eq)]
19 #[repr(transparent)]
20 pub struct MultiFieldsULE<const LEN: usize, Format: VarZeroVecFormat>(
21     VarZeroLengthlessSlice<[u8], Format>,
22 );
23 
24 impl<const LEN: usize, Format: VarZeroVecFormat> MultiFieldsULE<LEN, Format> {
25     /// Compute the amount of bytes needed to support elements with lengths `lengths`
26     #[inline]
27     #[allow(clippy::expect_used)] // See #1410
compute_encoded_len_for(lengths: [usize; LEN]) -> usize28     pub fn compute_encoded_len_for(lengths: [usize; LEN]) -> usize {
29         let lengths = lengths.map(BlankSliceEncoder);
30         crate::varzerovec::components::compute_serializable_len_without_length::<_, _, Format>(
31             &lengths,
32         )
33         .expect("Too many bytes to encode") as usize
34     }
35 
36     /// Construct a partially initialized MultiFieldsULE backed by a mutable byte buffer
new_from_lengths_partially_initialized<'a>( lengths: [usize; LEN], output: &'a mut [u8], ) -> &'a mut Self37     pub fn new_from_lengths_partially_initialized<'a>(
38         lengths: [usize; LEN],
39         output: &'a mut [u8],
40     ) -> &'a mut Self {
41         let lengths = lengths.map(BlankSliceEncoder);
42         crate::varzerovec::components::write_serializable_bytes_without_length::<_, _, Format>(
43             &lengths, output,
44         );
45         debug_assert!(
46             <VarZeroLengthlessSlice<[u8], Format>>::parse_bytes(LEN as u32, output).is_ok(),
47             "Encoded slice must be valid VarZeroSlice"
48         );
49         unsafe {
50             // Safe since write_serializable_bytes produces a valid VarZeroLengthlessSlice buffer with the right format
51             let slice = <VarZeroLengthlessSlice<[u8], Format>>::from_bytes_unchecked_mut(output);
52             // safe since `Self` is transparent over VarZeroLengthlessSlice<[u8], Format>
53             mem::transmute::<&mut VarZeroLengthlessSlice<[u8], Format>, &mut Self>(slice)
54         }
55     }
56 
57     /// Given a buffer of size obtained by [`Self::compute_encoded_len_for()`], write element A to index idx
58     ///
59     /// # Safety
60     /// - `idx` must be in range
61     /// - `T` must be the appropriate type expected by the custom derive in this usage of this type
62     #[inline]
set_field_at<T: VarULE + ?Sized, A: EncodeAsVarULE<T> + ?Sized>( &mut self, idx: usize, value: &A, )63     pub unsafe fn set_field_at<T: VarULE + ?Sized, A: EncodeAsVarULE<T> + ?Sized>(
64         &mut self,
65         idx: usize,
66         value: &A,
67     ) {
68         value.encode_var_ule_write(self.0.get_bytes_at_mut(LEN as u32, idx))
69     }
70 
71     /// Validate field at `index` to see if it is a valid `T` VarULE type
72     ///
73     /// # Safety
74     ///
75     /// - `index` must be in range
76     #[inline]
validate_field<T: VarULE + ?Sized>(&self, index: usize) -> Result<(), UleError>77     pub unsafe fn validate_field<T: VarULE + ?Sized>(&self, index: usize) -> Result<(), UleError> {
78         T::validate_bytes(self.0.get_unchecked(LEN as u32, index))
79     }
80 
81     /// Get field at `index` as a value of type T
82     ///
83     /// # Safety
84     ///
85     /// - `index` must be in range
86     /// - Element at `index` must have been created with the VarULE type T
87     #[inline]
get_field<T: VarULE + ?Sized>(&self, index: usize) -> &T88     pub unsafe fn get_field<T: VarULE + ?Sized>(&self, index: usize) -> &T {
89         T::from_bytes_unchecked(self.0.get_unchecked(LEN as u32, index))
90     }
91 
92     /// Construct from a byte slice
93     ///
94     /// # Safety
95     /// - byte slice must be a valid VarZeroLengthlessSlice<[u8], Format> with length LEN
96     #[inline]
from_bytes_unchecked(bytes: &[u8]) -> &Self97     pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
98         // &Self is transparent over &VZS<..> with the right format
99         mem::transmute(<VarZeroLengthlessSlice<[u8], Format>>::from_bytes_unchecked(bytes))
100     }
101 
102     /// Get the bytes behind this value
as_bytes(&self) -> &[u8]103     pub fn as_bytes(&self) -> &[u8] {
104         self.0.as_bytes()
105     }
106 }
107 
108 impl<const LEN: usize, Format: VarZeroVecFormat> fmt::Debug for MultiFieldsULE<LEN, Format> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result109     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110         write!(f, "MultiFieldsULE<{LEN}>({:?})", self.0.as_bytes())
111     }
112 }
113 /// This lets us conveniently use the EncodeAsVarULE functionality to create
114 /// `VarZeroVec<[u8]>`s that have the right amount of space for elements
115 /// without having to duplicate any unsafe code
116 #[repr(transparent)]
117 struct BlankSliceEncoder(usize);
118 
119 unsafe impl EncodeAsVarULE<[u8]> for BlankSliceEncoder {
encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R120     fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R {
121         // unnecessary if the other two are implemented
122         unreachable!()
123     }
124 
125     #[inline]
encode_var_ule_len(&self) -> usize126     fn encode_var_ule_len(&self) -> usize {
127         self.0
128     }
129 
130     #[inline]
encode_var_ule_write(&self, _dst: &mut [u8])131     fn encode_var_ule_write(&self, _dst: &mut [u8]) {
132         // do nothing
133     }
134 }
135 
136 // Safety (based on the safety checklist on the VarULE trait):
137 //  1. MultiFieldsULE does not include any uninitialized or padding bytes (achieved by being transparent over a VarULE type)
138 //  2. MultiFieldsULE is aligned to 1 byte (achieved by being transparent over a VarULE type)
139 //  3. The impl of `validate_bytes()` returns an error if any byte is not valid.
140 //  4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety
141 //  5. The impl of `from_bytes_unchecked()` returns a reference to the same data.
142 //  6. All other methods are defaulted
143 //  7. `MultiFieldsULE` byte equality is semantic equality (achieved by being transparent over a VarULE type)
144 unsafe impl<const LEN: usize, Format: VarZeroVecFormat> VarULE for MultiFieldsULE<LEN, Format> {
145     /// Note: MultiFieldsULE is usually used in cases where one should be calling .validate_field() directly for
146     /// each field, rather than using the regular VarULE impl.
147     ///
148     /// This impl exists so that EncodeAsVarULE can work.
149     #[inline]
validate_bytes(slice: &[u8]) -> Result<(), UleError>150     fn validate_bytes(slice: &[u8]) -> Result<(), UleError> {
151         <VarZeroLengthlessSlice<[u8], Format>>::parse_bytes(LEN as u32, slice).map(|_| ())
152     }
153 
154     #[inline]
from_bytes_unchecked(bytes: &[u8]) -> &Self155     unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
156         // &Self is transparent over &VZS<..>
157         mem::transmute(<VarZeroLengthlessSlice<[u8], Format>>::from_bytes_unchecked(bytes))
158     }
159 }
160