195 lines
5.3 KiB
Rust
195 lines
5.3 KiB
Rust
use crate::AllBytesValid;
|
|
use std::cmp::Ordering;
|
|
use std::fmt;
|
|
use std::mem;
|
|
use std::slice;
|
|
|
|
/// Helper type representing a 1-byte-aligned little-endian value in memory.
|
|
///
|
|
/// This type is used in slice types for Wasmtime host bindings. Guest types are
|
|
/// not guaranteed to be either aligned or in the native endianness. This type
|
|
/// wraps these types and provides explicit getters/setters to interact with the
|
|
/// underlying value in a safe host-agnostic manner.
|
|
#[repr(packed)]
|
|
pub struct Le<T>(T);
|
|
|
|
impl<T> Le<T>
|
|
where
|
|
T: Endian,
|
|
{
|
|
/// Creates a new `Le<T>` value where the internals are stored in a way
|
|
/// that's safe to copy into wasm linear memory.
|
|
pub fn new(t: T) -> Le<T> {
|
|
Le(t.into_le())
|
|
}
|
|
|
|
/// Reads the value stored in this `Le<T>`.
|
|
///
|
|
/// This will perform a correct read even if the underlying memory is
|
|
/// unaligned, and it will also convert to the host's endianness for the
|
|
/// right representation of `T`.
|
|
pub fn get(&self) -> T {
|
|
self.0.from_le()
|
|
}
|
|
|
|
/// Writes the `val` to this slot.
|
|
///
|
|
/// This will work correctly even if the underlying memory is unaligned and
|
|
/// it will also automatically convert the `val` provided to an endianness
|
|
/// appropriate for WebAssembly (little-endian).
|
|
pub fn set(&mut self, val: T) {
|
|
self.0 = val.into_le();
|
|
}
|
|
|
|
pub(crate) fn from_slice(bytes: &[u8]) -> &[Le<T>] {
|
|
// SAFETY: The invariants we uphold here are:
|
|
//
|
|
// * the lifetime of the input is the same as the output, so we're only
|
|
// dealing with valid memory.
|
|
// * the alignment of the input is the same as the output (1)
|
|
// * the input isn't being truncated and we're consuming all of it (it
|
|
// must be a multiple of the size of `Le<T>`)
|
|
// * all byte-patterns for `Le<T>` are valid. This is guaranteed by the
|
|
// `AllBytesValid` supertrait of `Endian`.
|
|
unsafe {
|
|
assert_eq!(mem::align_of::<Le<T>>(), 1);
|
|
assert!(bytes.len() % mem::size_of::<Le<T>>() == 0);
|
|
fn all_bytes_valid<T: AllBytesValid>() {}
|
|
all_bytes_valid::<Le<T>>();
|
|
|
|
slice::from_raw_parts(
|
|
bytes.as_ptr().cast::<Le<T>>(),
|
|
bytes.len() / mem::size_of::<Le<T>>(),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn from_slice_mut(bytes: &mut [u8]) -> &mut [Le<T>] {
|
|
// SAFETY: see `from_slice` above
|
|
//
|
|
// Note that both the input and the output are `mut`, helping to
|
|
// maintain the guarantee of uniqueness.
|
|
unsafe {
|
|
assert_eq!(mem::align_of::<Le<T>>(), 1);
|
|
assert!(bytes.len() % mem::size_of::<Le<T>>() == 0);
|
|
slice::from_raw_parts_mut(
|
|
bytes.as_mut_ptr().cast::<Le<T>>(),
|
|
bytes.len() / mem::size_of::<Le<T>>(),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Copy> Clone for Le<T> {
|
|
fn clone(&self) -> Self {
|
|
*self
|
|
}
|
|
}
|
|
|
|
impl<T: Copy> Copy for Le<T> {}
|
|
|
|
impl<T: Endian + PartialEq> PartialEq for Le<T> {
|
|
fn eq(&self, other: &Le<T>) -> bool {
|
|
self.get() == other.get()
|
|
}
|
|
}
|
|
|
|
impl<T: Endian + PartialEq> PartialEq<T> for Le<T> {
|
|
fn eq(&self, other: &T) -> bool {
|
|
self.get() == *other
|
|
}
|
|
}
|
|
|
|
impl<T: Endian + Eq> Eq for Le<T> {}
|
|
|
|
impl<T: Endian + PartialOrd> PartialOrd for Le<T> {
|
|
fn partial_cmp(&self, other: &Le<T>) -> Option<Ordering> {
|
|
self.get().partial_cmp(&other.get())
|
|
}
|
|
}
|
|
|
|
impl<T: Endian + Ord> Ord for Le<T> {
|
|
fn cmp(&self, other: &Le<T>) -> Ordering {
|
|
self.get().cmp(&other.get())
|
|
}
|
|
}
|
|
|
|
impl<T: Endian + fmt::Debug> fmt::Debug for Le<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.get().fmt(f)
|
|
}
|
|
}
|
|
|
|
impl<T: Endian> From<T> for Le<T> {
|
|
fn from(t: T) -> Le<T> {
|
|
Le::new(t)
|
|
}
|
|
}
|
|
|
|
unsafe impl<T: AllBytesValid> AllBytesValid for Le<T> {}
|
|
|
|
/// Trait used for the implementation of the `Le` type.
|
|
pub trait Endian: AllBytesValid + Copy + Sized {
|
|
/// Converts this value and any aggregate fields (if any) into little-endian
|
|
/// byte order
|
|
fn into_le(self) -> Self;
|
|
/// Converts this value and any aggregate fields (if any) from
|
|
/// little-endian byte order
|
|
fn from_le(self) -> Self;
|
|
}
|
|
|
|
macro_rules! primitives {
|
|
($($t:ident)*) => ($(
|
|
impl Endian for $t {
|
|
#[inline]
|
|
fn into_le(self) -> Self {
|
|
Self::from_ne_bytes(self.to_le_bytes())
|
|
}
|
|
|
|
#[inline]
|
|
fn from_le(self) -> Self {
|
|
Self::from_le_bytes(self.to_ne_bytes())
|
|
}
|
|
}
|
|
)*)
|
|
}
|
|
|
|
primitives! {
|
|
u8 i8
|
|
u16 i16
|
|
u32 i32
|
|
u64 i64
|
|
f32 f64
|
|
}
|
|
|
|
macro_rules! tuples {
|
|
($(($($t:ident)*))*) => ($(
|
|
#[allow(non_snake_case)]
|
|
impl <$($t:Endian,)*> Endian for ($($t,)*) {
|
|
fn into_le(self) -> Self {
|
|
let ($($t,)*) = self;
|
|
($($t.into_le(),)*)
|
|
}
|
|
|
|
fn from_le(self) -> Self {
|
|
let ($($t,)*) = self;
|
|
($($t.from_le(),)*)
|
|
}
|
|
}
|
|
)*)
|
|
}
|
|
|
|
tuples! {
|
|
()
|
|
(T1)
|
|
(T1 T2)
|
|
(T1 T2 T3)
|
|
(T1 T2 T3 T4)
|
|
(T1 T2 T3 T4 T5)
|
|
(T1 T2 T3 T4 T5 T6)
|
|
(T1 T2 T3 T4 T5 T6 T7)
|
|
(T1 T2 T3 T4 T5 T6 T7 T8)
|
|
(T1 T2 T3 T4 T5 T6 T7 T8 T9)
|
|
(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10)
|
|
}
|