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::Index32;
7use crate::VarZeroSlice;
8use core::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.
13///
14/// Logically, consider it to be `(V1, V2, V3, ..)`
15/// where `V1` etc are potentially different [`VarULE`] types.
16///
17/// Internally, it is represented by a VarZeroSlice.
18#[derive(PartialEq, Eq, Debug)]
19#[repr(transparent)]
20pub struct MultiFieldsULE(VarZeroSlice<[u8], Index32>);
21
22impl MultiFieldsULE {
23 /// Compute the amount of bytes needed to support elements with lengths `lengths`
24 #[inline]
25 pub fn compute_encoded_len_for(lengths: &[usize]) -> usize {
26 #[allow(clippy::expect_used)] // See #1410
27 unsafe {
28 // safe since BlankSliceEncoder is transparent over usize
29 let lengths = &*(lengths as *const [usize] as *const [BlankSliceEncoder]);
30 crate::varzerovec::components::compute_serializable_len::<_, _, Index32>(lengths)
31 .expect("Too many bytes to encode") as usize
32 }
33 }
34
35 /// Construct a partially initialized MultiFieldsULE backed by a mutable byte buffer
36 pub fn new_from_lengths_partially_initialized<'a>(
37 lengths: &[usize],
38 output: &'a mut [u8],
39 ) -> &'a mut Self {
40 unsafe {
41 // safe since BlankSliceEncoder is transparent over usize
42 let lengths = &*(lengths as *const [usize] as *const [BlankSliceEncoder]);
43 crate::varzerovec::components::write_serializable_bytes::<_, _, Index32>(
44 lengths, output,
45 );
46 debug_assert!(
47 <VarZeroSlice<[u8], Index32>>::validate_byte_slice(output).is_ok(),
48 "Encoded slice must be valid VarZeroSlice"
49 );
50 // Safe since write_serializable_bytes produces a valid VarZeroSlice buffer
51 let slice = <VarZeroSlice<[u8], Index32>>::from_byte_slice_unchecked_mut(output);
52 // safe since `Self` is transparent over VarZeroSlice
53 mem::transmute::<&mut VarZeroSlice<_, Index32>, &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]
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(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]
77 pub unsafe fn validate_field<T: VarULE + ?Sized>(
78 &self,
79 index: usize,
80 ) -> Result<(), ZeroVecError> {
81 T::validate_byte_slice(self.0.get_unchecked(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_byte_slice_unchecked(self.0.get_unchecked(index))
93 }
94
95 /// Construct from a byte slice
96 ///
97 /// # Safety
98 /// - byte slice must be a valid VarZeroSlice<[u8]>
99 #[inline]
100 pub unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self {
101 // &Self is transparent over &VZS<..>
102 mem::transmute(<VarZeroSlice<[u8]>>::from_byte_slice_unchecked(bytes))
103 }
104}
105
106/// This lets us conveniently use the EncodeAsVarULE functionality to create
107/// `VarZeroVec<[u8]>`s that have the right amount of space for elements
108/// without having to duplicate any unsafe code
109#[repr(transparent)]
110struct BlankSliceEncoder(usize);
111
112unsafe impl EncodeAsVarULE<[u8]> for BlankSliceEncoder {
113 fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R {
114 // unnecessary if the other two are implemented
115 unreachable!()
116 }
117
118 #[inline]
119 fn encode_var_ule_len(&self) -> usize {
120 self.0
121 }
122
123 #[inline]
124 fn encode_var_ule_write(&self, _dst: &mut [u8]) {
125 // do nothing
126 }
127}
128
129// Safety (based on the safety checklist on the VarULE trait):
130// 1. MultiFieldsULE does not include any uninitialized or padding bytes (achieved by being transparent over a VarULE type)
131// 2. MultiFieldsULE is aligned to 1 byte (achieved by being transparent over a VarULE type)
132// 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid.
133// 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety
134// 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data.
135// 6. All other methods are defaulted
136// 7. `MultiFieldsULE` byte equality is semantic equality (achieved by being transparent over a VarULE type)
137unsafe impl VarULE for MultiFieldsULE {
138 /// Note: MultiFieldsULE is usually used in cases where one should be calling .validate_field() directly for
139 /// each field, rather than using the regular VarULE impl.
140 ///
141 /// This impl exists so that EncodeAsVarULE can work.
142 #[inline]
143 fn validate_byte_slice(slice: &[u8]) -> Result<(), ZeroVecError> {
144 <VarZeroSlice<[u8], Index32>>::validate_byte_slice(_bytes:slice)
145 }
146
147 #[inline]
148 unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self {
149 // &Self is transparent over &VZS<..>
150 mem::transmute(<VarZeroSlice<[u8], Index32>>::from_byte_slice_unchecked(
151 bytes,
152 ))
153 }
154}
155