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