blob: 3281b2088825cd144835b6ed8ce2b0947d487eae [file] [log] [blame]
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use super::*;
use crate::varzerovec::Index32;
use crate::VarZeroSlice;
use core::mem;
/// This type is used by the custom derive to represent multiple [`VarULE`]
/// fields packed into a single end-of-struct field. It is not recommended
/// to use this type directly.
///
/// Logically, consider it to be `(V1, V2, V3, ..)`
/// where `V1` etc are potentially different [`VarULE`] types.
///
/// Internally, it is represented by a VarZeroSlice.
#[derive(PartialEq, Eq, Debug)]
#[repr(transparent)]
pub struct MultiFieldsULE(VarZeroSlice<[u8], Index32>);
impl MultiFieldsULE {
/// Compute the amount of bytes needed to support elements with lengths `lengths`
#[inline]
pub fn compute_encoded_len_for(lengths: &[usize]) -> usize {
#[allow(clippy::expect_used)] // See #1410
unsafe {
// safe since BlankSliceEncoder is transparent over usize
let lengths = &*(lengths as *const [usize] as *const [BlankSliceEncoder]);
crate::varzerovec::components::compute_serializable_len::<_, _, Index32>(lengths)
.expect("Too many bytes to encode") as usize
}
}
/// Construct a partially initialized MultiFieldsULE backed by a mutable byte buffer
pub fn new_from_lengths_partially_initialized<'a>(
lengths: &[usize],
output: &'a mut [u8],
) -> &'a mut Self {
unsafe {
// safe since BlankSliceEncoder is transparent over usize
let lengths = &*(lengths as *const [usize] as *const [BlankSliceEncoder]);
crate::varzerovec::components::write_serializable_bytes::<_, _, Index32>(
lengths, output,
);
debug_assert!(
<VarZeroSlice<[u8], Index32>>::validate_byte_slice(output).is_ok(),
"Encoded slice must be valid VarZeroSlice"
);
// Safe since write_serializable_bytes produces a valid VarZeroSlice buffer
let slice = <VarZeroSlice<[u8], Index32>>::from_byte_slice_unchecked_mut(output);
// safe since `Self` is transparent over VarZeroSlice
mem::transmute::<&mut VarZeroSlice<_, Index32>, &mut Self>(slice)
}
}
/// Given a buffer of size obtained by [`Self::compute_encoded_len_for()`], write element A to index idx
///
/// # Safety
/// - `idx` must be in range
/// - `T` must be the appropriate type expected by the custom derive in this usage of this type
#[inline]
pub unsafe fn set_field_at<T: VarULE + ?Sized, A: EncodeAsVarULE<T> + ?Sized>(
&mut self,
idx: usize,
value: &A,
) {
value.encode_var_ule_write(self.0.get_bytes_at_mut(idx))
}
/// Validate field at `index` to see if it is a valid `T` VarULE type
///
/// # Safety
///
/// - `index` must be in range
#[inline]
pub unsafe fn validate_field<T: VarULE + ?Sized>(
&self,
index: usize,
) -> Result<(), ZeroVecError> {
T::validate_byte_slice(self.0.get_unchecked(index))
}
/// Get field at `index` as a value of type T
///
/// # Safety
///
/// - `index` must be in range
/// - Element at `index` must have been created with the VarULE type T
#[inline]
pub unsafe fn get_field<T: VarULE + ?Sized>(&self, index: usize) -> &T {
T::from_byte_slice_unchecked(self.0.get_unchecked(index))
}
/// Construct from a byte slice
///
/// # Safety
/// - byte slice must be a valid VarZeroSlice<[u8]>
#[inline]
pub unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self {
// &Self is transparent over &VZS<..>
mem::transmute(<VarZeroSlice<[u8]>>::from_byte_slice_unchecked(bytes))
}
}
/// This lets us conveniently use the EncodeAsVarULE functionality to create
/// `VarZeroVec<[u8]>`s that have the right amount of space for elements
/// without having to duplicate any unsafe code
#[repr(transparent)]
struct BlankSliceEncoder(usize);
unsafe impl EncodeAsVarULE<[u8]> for BlankSliceEncoder {
fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R {
// unnecessary if the other two are implemented
unreachable!()
}
#[inline]
fn encode_var_ule_len(&self) -> usize {
self.0
}
#[inline]
fn encode_var_ule_write(&self, _dst: &mut [u8]) {
// do nothing
}
}
// Safety (based on the safety checklist on the VarULE trait):
// 1. MultiFieldsULE does not include any uninitialized or padding bytes (achieved by being transparent over a VarULE type)
// 2. MultiFieldsULE is aligned to 1 byte (achieved by being transparent over a VarULE type)
// 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid.
// 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety
// 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data.
// 6. All other methods are defaulted
// 7. `MultiFieldsULE` byte equality is semantic equality (achieved by being transparent over a VarULE type)
unsafe impl VarULE for MultiFieldsULE {
/// Note: MultiFieldsULE is usually used in cases where one should be calling .validate_field() directly for
/// each field, rather than using the regular VarULE impl.
///
/// This impl exists so that EncodeAsVarULE can work.
#[inline]
fn validate_byte_slice(slice: &[u8]) -> Result<(), ZeroVecError> {
<VarZeroSlice<[u8], Index32>>::validate_byte_slice(slice)
}
#[inline]
unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self {
// &Self is transparent over &VZS<..>
mem::transmute(<VarZeroSlice<[u8], Index32>>::from_byte_slice_unchecked(
bytes,
))
}
}