zerovec 0.9.2

Zero-copy vector backed by a byte array
Documentation
// 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)]
#[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]>>::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]>>::validate_byte_slice(slice)
    }

    #[inline]
    unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self {
        // &Self is transparent over &VZS<..>
        mem::transmute(<VarZeroSlice<[u8]>>::from_byte_slice_unchecked(bytes))
    }
}