use std::{fmt::Debug, hash::Hash};
use crate::{ir::value::ConstValue, PointerSize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VectorLaneKind {
Integer,
Float,
NativeInteger,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VectorShape {
pub lane_count: u32,
pub lane_kind: VectorLaneKind,
pub lane_bits: u32,
pub total_bits: u32,
}
impl VectorShape {
#[must_use]
pub const fn new(
lane_count: u32,
lane_kind: VectorLaneKind,
lane_bits: u32,
total_bits: u32,
) -> Option<Self> {
if lane_count == 0 || lane_bits == 0 || total_bits == 0 {
return None;
}
if lane_count.saturating_mul(lane_bits) != total_bits {
return None;
}
Some(Self {
lane_count,
lane_kind,
lane_bits,
total_bits,
})
}
#[must_use]
pub const fn is_valid(self) -> bool {
self.lane_count != 0
&& self.lane_bits != 0
&& self.total_bits != 0
&& self.lane_count.saturating_mul(self.lane_bits) == self.total_bits
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VectorMaskShape {
pub lane_count: u32,
pub lane_bits: u32,
}
impl VectorMaskShape {
#[must_use]
pub const fn new(lane_count: u32, lane_bits: u32) -> Option<Self> {
if lane_count == 0 || lane_bits == 0 {
return None;
}
Some(Self {
lane_count,
lane_bits,
})
}
#[must_use]
pub const fn is_valid(self) -> bool {
self.lane_count != 0 && self.lane_bits != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VectorLengthMultiplier {
pub numerator: u32,
pub denominator: u32,
}
impl VectorLengthMultiplier {
#[must_use]
pub const fn new(numerator: u32, denominator: u32) -> Option<Self> {
if numerator == 0 || denominator == 0 {
return None;
}
Some(Self {
numerator,
denominator,
})
}
#[must_use]
pub const fn one() -> Self {
Self {
numerator: 1,
denominator: 1,
}
}
#[must_use]
pub const fn is_valid(self) -> bool {
self.numerator != 0 && self.denominator != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VectorTailPolicy {
Agnostic,
Undisturbed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VectorMaskPolicy {
Agnostic,
Undisturbed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ScalableVectorShape {
pub min_lane_count: u32,
pub lane_kind: VectorLaneKind,
pub lane_bits: u32,
pub length_multiplier: VectorLengthMultiplier,
pub tail_policy: VectorTailPolicy,
pub mask_policy: VectorMaskPolicy,
}
impl ScalableVectorShape {
#[must_use]
pub const fn new(
min_lane_count: u32,
lane_kind: VectorLaneKind,
lane_bits: u32,
length_multiplier: VectorLengthMultiplier,
tail_policy: VectorTailPolicy,
mask_policy: VectorMaskPolicy,
) -> Option<Self> {
if min_lane_count == 0 || lane_bits == 0 || !length_multiplier.is_valid() {
return None;
}
Some(Self {
min_lane_count,
lane_kind,
lane_bits,
length_multiplier,
tail_policy,
mask_policy,
})
}
#[must_use]
pub const fn is_valid(self) -> bool {
self.min_lane_count != 0 && self.lane_bits != 0 && self.length_multiplier.is_valid()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ScalableVectorMaskShape {
pub min_lane_count: u32,
pub lane_bits: u32,
pub length_multiplier: VectorLengthMultiplier,
}
impl ScalableVectorMaskShape {
#[must_use]
pub const fn new(
min_lane_count: u32,
lane_bits: u32,
length_multiplier: VectorLengthMultiplier,
) -> Option<Self> {
if min_lane_count == 0 || lane_bits == 0 || !length_multiplier.is_valid() {
return None;
}
Some(Self {
min_lane_count,
lane_bits,
length_multiplier,
})
}
#[must_use]
pub const fn is_valid(self) -> bool {
self.min_lane_count != 0 && self.lane_bits != 0 && self.length_multiplier.is_valid()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VectorDescriptor {
Fixed(VectorShape),
Scalable(ScalableVectorShape),
}
impl VectorDescriptor {
#[must_use]
pub const fn lane_kind(self) -> VectorLaneKind {
match self {
Self::Fixed(shape) => shape.lane_kind,
Self::Scalable(shape) => shape.lane_kind,
}
}
#[must_use]
pub const fn lane_bits(self) -> u32 {
match self {
Self::Fixed(shape) => shape.lane_bits,
Self::Scalable(shape) => shape.lane_bits,
}
}
#[must_use]
pub const fn fixed_lane_count(self) -> Option<u32> {
match self {
Self::Fixed(shape) => Some(shape.lane_count),
Self::Scalable(_) => None,
}
}
#[must_use]
pub const fn min_lane_count(self) -> u32 {
match self {
Self::Fixed(shape) => shape.lane_count,
Self::Scalable(shape) => shape.min_lane_count,
}
}
#[must_use]
pub const fn total_bits(self) -> Option<u32> {
match self {
Self::Fixed(shape) => Some(shape.total_bits),
Self::Scalable(_) => None,
}
}
#[must_use]
pub const fn mask_descriptor(self) -> VectorMaskDescriptor {
match self {
Self::Fixed(shape) => VectorMaskDescriptor::Fixed(VectorMaskShape {
lane_count: shape.lane_count,
lane_bits: 1,
}),
Self::Scalable(shape) => VectorMaskDescriptor::Scalable(ScalableVectorMaskShape {
min_lane_count: shape.min_lane_count,
lane_bits: 1,
length_multiplier: shape.length_multiplier,
}),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VectorMaskDescriptor {
Fixed(VectorMaskShape),
Scalable(ScalableVectorMaskShape),
}
impl VectorMaskDescriptor {
#[must_use]
pub const fn fixed_lane_count(self) -> Option<u32> {
match self {
Self::Fixed(shape) => Some(shape.lane_count),
Self::Scalable(_) => None,
}
}
#[must_use]
pub const fn min_lane_count(self) -> u32 {
match self {
Self::Fixed(shape) => shape.lane_count,
Self::Scalable(shape) => shape.min_lane_count,
}
}
#[must_use]
pub const fn lane_bits(self) -> u32 {
match self {
Self::Fixed(shape) => shape.lane_bits,
Self::Scalable(shape) => shape.lane_bits,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VectorShuffleLane {
Undef,
Zero,
Left(u32),
Right(u32),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VectorShuffleMask {
lanes: Vec<VectorShuffleLane>,
}
impl VectorShuffleMask {
#[must_use]
pub fn new(lanes: Vec<VectorShuffleLane>) -> Self {
Self { lanes }
}
#[must_use]
pub fn lanes(&self) -> &[VectorShuffleLane] {
&self.lanes
}
#[must_use]
pub fn is_valid_for(&self, left_lanes: u32, right_lanes: Option<u32>) -> bool {
if self.lanes.is_empty() {
return false;
}
self.lanes.iter().all(|lane| match *lane {
VectorShuffleLane::Undef | VectorShuffleLane::Zero => true,
VectorShuffleLane::Left(idx) => idx < left_lanes,
VectorShuffleLane::Right(idx) => right_lanes.is_some_and(|count| idx < count),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Endianness {
Little,
Big,
}
impl Endianness {
#[must_use]
pub const fn is_little(self) -> bool {
matches!(self, Self::Little)
}
#[must_use]
pub const fn is_big(self) -> bool {
matches!(self, Self::Big)
}
#[must_use]
pub fn to_native_u16(self, value: u16) -> u16 {
match self {
Self::Little => u16::from_le(value),
Self::Big => u16::from_be(value),
}
}
#[must_use]
pub fn to_native_u32(self, value: u32) -> u32 {
match self {
Self::Little => u32::from_le(value),
Self::Big => u32::from_be(value),
}
}
#[must_use]
pub fn to_native_u64(self, value: u64) -> u64 {
match self {
Self::Little => u64::from_le(value),
Self::Big => u64::from_be(value),
}
}
#[must_use]
pub fn to_native_u128(self, value: u128) -> u128 {
match self {
Self::Little => u128::from_le(value),
Self::Big => u128::from_be(value),
}
}
#[must_use]
pub fn from_native_u16(self, value: u16) -> u16 {
match self {
Self::Little => u16::to_le(value),
Self::Big => u16::to_be(value),
}
}
#[must_use]
pub fn from_native_u32(self, value: u32) -> u32 {
match self {
Self::Little => u32::to_le(value),
Self::Big => u32::to_be(value),
}
}
#[must_use]
pub fn from_native_u64(self, value: u64) -> u64 {
match self {
Self::Little => u64::to_le(value),
Self::Big => u64::to_be(value),
}
}
#[must_use]
pub fn from_native_u128(self, value: u128) -> u128 {
match self {
Self::Little => u128::to_le(value),
Self::Big => u128::to_be(value),
}
}
#[must_use]
pub fn bytes_of_u16(self, value: u16) -> [u8; 2] {
self.from_native_u16(value).to_ne_bytes()
}
#[must_use]
pub fn bytes_of_u32(self, value: u32) -> [u8; 4] {
self.from_native_u32(value).to_ne_bytes()
}
#[must_use]
pub fn bytes_of_u64(self, value: u64) -> [u8; 8] {
self.from_native_u64(value).to_ne_bytes()
}
#[must_use]
pub fn bytes_of_u128(self, value: u128) -> [u8; 16] {
self.from_native_u128(value).to_ne_bytes()
}
#[must_use]
pub fn read_u16(self, bytes: &[u8]) -> u16 {
let arr: [u8; 2] = match bytes.try_into() {
Ok(a) => a,
Err(_) => return 0,
};
self.to_native_u16(u16::from_ne_bytes(arr))
}
#[must_use]
pub fn read_u32(self, bytes: &[u8]) -> u32 {
let arr: [u8; 4] = match bytes.try_into() {
Ok(a) => a,
Err(_) => return 0,
};
self.to_native_u32(u32::from_ne_bytes(arr))
}
#[must_use]
pub fn read_u64(self, bytes: &[u8]) -> u64 {
let arr: [u8; 8] = match bytes.try_into() {
Ok(a) => a,
Err(_) => return 0,
};
self.to_native_u64(u64::from_ne_bytes(arr))
}
#[must_use]
pub fn read_u128(self, bytes: &[u8]) -> u128 {
let arr: [u8; 16] = match bytes.try_into() {
Ok(a) => a,
Err(_) => return 0,
};
self.to_native_u128(u128::from_ne_bytes(arr))
}
#[must_use]
pub fn bytes_of_ptr_sized(self, value: u64, ptr_size: PointerSize) -> Vec<u8> {
let masked = ptr_size.mask_unsigned(value);
match ptr_size {
PointerSize::Bit8 => vec![masked as u8],
PointerSize::Bit16 => self.bytes_of_u16(masked as u16).to_vec(),
PointerSize::Bit32 => self.bytes_of_u32(masked as u32).to_vec(),
PointerSize::Bit64 => self.bytes_of_u64(masked).to_vec(),
PointerSize::Bit128 => {
let v128 = ptr_size.mask_unsigned_128(u128::from(masked));
self.bytes_of_u128(v128).to_vec()
}
}
}
#[must_use]
pub fn read_ptr_sized(self, bytes: &[u8], ptr_size: PointerSize) -> u64 {
match ptr_size {
PointerSize::Bit8 => u64::from(bytes.first().copied().unwrap_or(0)),
PointerSize::Bit16 => u64::from(self.read_u16(bytes)),
PointerSize::Bit32 => u64::from(self.read_u32(bytes)),
PointerSize::Bit64 => self.read_u64(bytes),
PointerSize::Bit128 => {
self.read_u128(bytes) as u64
}
}
}
}
pub trait Target: Clone + Debug + Eq + Hash + Sized + 'static {
type TypeRef: Clone + Eq + Hash + Debug;
type MethodRef: Clone + Eq + Hash + Debug;
type FieldRef: Clone + Eq + Hash + Debug;
type SigRef: Clone + Eq + Hash + Debug;
type ExceptionKind: Clone + Eq + Debug;
type Type: Clone + Eq + Hash + Debug;
type OriginalInstruction: Clone + Debug;
type LocalSignature: Clone + Debug;
type Capability: Copy + Eq + Hash + Debug + 'static;
fn ptr_bytes(&self) -> u32;
fn endianness(&self) -> Endianness {
Endianness::Little
}
fn synthetic_instruction() -> Self::OriginalInstruction;
fn unknown_type() -> Self::Type;
fn is_integer(t: &Self::Type) -> bool;
fn is_floating(t: &Self::Type) -> bool;
fn is_signed(t: &Self::Type) -> bool;
fn is_pointer(t: &Self::Type) -> bool;
fn is_reference(t: &Self::Type) -> bool;
fn is_unknown(t: &Self::Type) -> bool;
fn bit_width(t: &Self::Type) -> Option<u32>;
fn is_vector(t: &Self::Type) -> bool {
Self::vector_descriptor(t).is_some()
}
fn vector_shape(_t: &Self::Type) -> Option<VectorShape> {
None
}
fn scalable_vector_shape(_t: &Self::Type) -> Option<ScalableVectorShape> {
None
}
fn vector_descriptor(t: &Self::Type) -> Option<VectorDescriptor> {
Self::vector_shape(t)
.map(VectorDescriptor::Fixed)
.or_else(|| Self::scalable_vector_shape(t).map(VectorDescriptor::Scalable))
}
fn vector_type(_shape: VectorShape) -> Option<Self::Type> {
None
}
fn scalable_vector_type(_shape: ScalableVectorShape) -> Option<Self::Type> {
None
}
fn vector_lane_type(_shape: VectorShape) -> Option<Self::Type> {
None
}
fn scalable_vector_lane_type(_shape: ScalableVectorShape) -> Option<Self::Type> {
None
}
fn vector_descriptor_lane_type(shape: VectorDescriptor) -> Option<Self::Type> {
match shape {
VectorDescriptor::Fixed(shape) => Self::vector_lane_type(shape),
VectorDescriptor::Scalable(shape) => Self::scalable_vector_lane_type(shape),
}
}
fn vector_mask_shape(_t: &Self::Type) -> Option<VectorMaskShape> {
None
}
fn scalable_vector_mask_shape(_t: &Self::Type) -> Option<ScalableVectorMaskShape> {
None
}
fn vector_mask_descriptor(t: &Self::Type) -> Option<VectorMaskDescriptor> {
Self::vector_mask_shape(t)
.map(VectorMaskDescriptor::Fixed)
.or_else(|| Self::scalable_vector_mask_shape(t).map(VectorMaskDescriptor::Scalable))
}
fn vector_mask_type(_shape: VectorMaskShape) -> Option<Self::Type> {
None
}
fn scalable_vector_mask_type(_shape: ScalableVectorMaskShape) -> Option<Self::Type> {
None
}
fn vector_mask_descriptor_type(shape: VectorMaskDescriptor) -> Option<Self::Type> {
match shape {
VectorMaskDescriptor::Fixed(shape) => Self::vector_mask_type(shape),
VectorMaskDescriptor::Scalable(shape) => Self::scalable_vector_mask_type(shape),
}
}
fn instruction_mnemonic(instr: &Self::OriginalInstruction) -> &'static str;
fn instruction_rva(instr: &Self::OriginalInstruction) -> u64;
fn is_filter_handler(flags: &Self::ExceptionKind) -> bool;
fn result_type_for_const(_value: &ConstValue<Self>) -> Option<Self::Type> {
None
}
fn comparison_result_type() -> Option<Self::Type> {
None
}
fn arithmetic_result_type() -> Option<Self::Type> {
None
}
fn native_int_result_type() -> Option<Self::Type> {
None
}
fn ckfinite_result_type() -> Option<Self::Type> {
None
}
fn function_ptr_result_type() -> Option<Self::Type> {
None
}
fn object_result_type() -> Option<Self::Type> {
None
}
fn value_type_from_ref(_r: &Self::TypeRef) -> Option<Self::Type> {
None
}
fn byref_value_type_from_ref(_r: &Self::TypeRef) -> Option<Self::Type> {
None
}
fn byref_class_type_from_ref(_r: &Self::TypeRef) -> Option<Self::Type> {
None
}
fn convert_const(
_value: &ConstValue<Self>,
_target_type: &Self::Type,
_unsigned_source: bool,
_ptr_bytes: u32,
) -> Option<ConstValue<Self>> {
None
}
fn convert_const_checked(
_value: &ConstValue<Self>,
_target_type: &Self::Type,
_unsigned_source: bool,
_ptr_bytes: u32,
) -> Option<ConstValue<Self>> {
None
}
fn evaluate_int_conv(
_value: i64,
_target_type: &Self::Type,
_unsigned: bool,
_ptr_bytes: u32,
) -> Option<ConstValue<Self>> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn little_is_little_not_big() {
assert!(Endianness::Little.is_little());
assert!(!Endianness::Little.is_big());
}
#[test]
fn big_is_big_not_little() {
assert!(Endianness::Big.is_big());
assert!(!Endianness::Big.is_little());
}
#[test]
fn little_to_native_u16_is_le() {
let value = 0x0102_u16;
assert_eq!(Endianness::Little.to_native_u16(value), u16::from_le(value));
}
#[test]
fn big_to_native_u16_is_be() {
let value = 0x0102_u16;
assert_eq!(Endianness::Big.to_native_u16(value), u16::from_be(value));
}
#[test]
fn little_to_native_u32_is_le() {
let value = 0x01020304_u32;
assert_eq!(Endianness::Little.to_native_u32(value), u32::from_le(value));
}
#[test]
fn big_to_native_u32_is_be() {
let value = 0x01020304_u32;
assert_eq!(Endianness::Big.to_native_u32(value), u32::from_be(value));
}
#[test]
fn little_to_native_u64_is_le() {
let value = 0x0102030405060708_u64;
assert_eq!(Endianness::Little.to_native_u64(value), u64::from_le(value));
}
#[test]
fn big_to_native_u64_is_be() {
let value = 0x0102030405060708_u64;
assert_eq!(Endianness::Big.to_native_u64(value), u64::from_be(value));
}
#[test]
fn little_to_native_u128_is_le() {
let value = 0x0102030405060708090a0b0c0d0e0f10_u128;
assert_eq!(
Endianness::Little.to_native_u128(value),
u128::from_le(value)
);
}
#[test]
fn big_to_native_u128_is_be() {
let value = 0x0102030405060708090a0b0c0d0e0f10_u128;
assert_eq!(Endianness::Big.to_native_u128(value), u128::from_be(value));
}
#[test]
fn little_from_native_u16_is_le() {
let value = 0x0102_u16;
assert_eq!(Endianness::Little.from_native_u16(value), u16::to_le(value));
}
#[test]
fn big_from_native_u16_is_be() {
let value = 0x0102_u16;
assert_eq!(Endianness::Big.from_native_u16(value), u16::to_be(value));
}
#[test]
fn from_native_round_trips_through_to_native() {
let value = 0xdeadbeef_u32;
for endianness in [Endianness::Little, Endianness::Big] {
let converted = endianness.from_native_u32(value);
let restored = endianness.to_native_u32(converted);
assert_eq!(restored, value, "round-trip failed for {endianness:?}");
}
}
#[test]
fn little_bytes_of_u16_match_le_byte_order() {
assert_eq!(Endianness::Little.bytes_of_u16(0x0102), [0x02, 0x01]);
}
#[test]
fn big_bytes_of_u16_match_be_byte_order() {
assert_eq!(Endianness::Big.bytes_of_u16(0x0102), [0x01, 0x02]);
}
#[test]
fn little_bytes_of_u32_match_le_byte_order() {
assert_eq!(
Endianness::Little.bytes_of_u32(0x01020304),
[0x04, 0x03, 0x02, 0x01]
);
}
#[test]
fn big_bytes_of_u32_match_be_byte_order() {
assert_eq!(
Endianness::Big.bytes_of_u32(0x01020304),
[0x01, 0x02, 0x03, 0x04]
);
}
#[test]
fn little_bytes_of_u64_match_le_byte_order() {
assert_eq!(
Endianness::Little.bytes_of_u64(0x0102030405060708),
[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]
);
}
#[test]
fn big_bytes_of_u64_match_be_byte_order() {
assert_eq!(
Endianness::Big.bytes_of_u64(0x0102030405060708),
[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
);
}
#[test]
fn little_bytes_of_u128_match_le_byte_order() {
let bytes = Endianness::Little.bytes_of_u128(0x0102030405060708090a0b0c0d0e0f10);
assert_eq!(
bytes,
[
0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03,
0x02, 0x01,
]
);
}
#[test]
fn big_bytes_of_u128_match_be_byte_order() {
let bytes = Endianness::Big.bytes_of_u128(0x0102030405060708090a0b0c0d0e0f10);
assert_eq!(
bytes,
[
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10,
]
);
}
#[test]
fn read_u16_round_trips_with_bytes_of_u16() {
let value = 0xabcd_u16;
for endianness in [Endianness::Little, Endianness::Big] {
let bytes = endianness.bytes_of_u16(value);
let restored = endianness.read_u16(&bytes);
assert_eq!(
restored, value,
"read_u16 round-trip failed for {endianness:?}"
);
}
}
#[test]
fn read_u32_round_trips_with_bytes_of_u32() {
let value = 0xdeadbeef_u32;
for endianness in [Endianness::Little, Endianness::Big] {
let bytes = endianness.bytes_of_u32(value);
let restored = endianness.read_u32(&bytes);
assert_eq!(
restored, value,
"read_u32 round-trip failed for {endianness:?}"
);
}
}
#[test]
fn read_u64_round_trips_with_bytes_of_u64() {
let value = 0xdeadbeef_cafebabe_u64;
for endianness in [Endianness::Little, Endianness::Big] {
let bytes = endianness.bytes_of_u64(value);
let restored = endianness.read_u64(&bytes);
assert_eq!(
restored, value,
"read_u64 round-trip failed for {endianness:?}"
);
}
}
#[test]
fn read_u128_round_trips_with_bytes_of_u128() {
let value = 0xdeadbeef_cafebabe_01020304_05060708_u128;
for endianness in [Endianness::Little, Endianness::Big] {
let bytes = endianness.bytes_of_u128(value);
let restored = endianness.read_u128(&bytes);
assert_eq!(
restored, value,
"read_u128 round-trip failed for {endianness:?}"
);
}
}
#[test]
fn bytes_of_ptr_sized_bit8_is_always_one_byte() {
let value = 0xAB;
for endianness in [Endianness::Little, Endianness::Big] {
let bytes = endianness.bytes_of_ptr_sized(value, PointerSize::Bit8);
assert_eq!(bytes, vec![0xAB], "Bit8 differs for {endianness:?}");
}
}
#[test]
fn bytes_of_ptr_sized_bit16_depends_on_endianness() {
let value = 0x0102;
assert_eq!(
Endianness::Little.bytes_of_ptr_sized(value, PointerSize::Bit16),
vec![0x02, 0x01],
);
assert_eq!(
Endianness::Big.bytes_of_ptr_sized(value, PointerSize::Bit16),
vec![0x01, 0x02],
);
}
#[test]
fn bytes_of_ptr_sized_bit32_depends_on_endianness() {
let value = 0x01020304;
assert_eq!(
Endianness::Little.bytes_of_ptr_sized(value, PointerSize::Bit32),
vec![0x04, 0x03, 0x02, 0x01],
);
assert_eq!(
Endianness::Big.bytes_of_ptr_sized(value, PointerSize::Bit32),
vec![0x01, 0x02, 0x03, 0x04],
);
}
#[test]
fn bytes_of_ptr_sized_bit64_depends_on_endianness() {
let value = 0x0102030405060708;
assert_eq!(
Endianness::Little.bytes_of_ptr_sized(value, PointerSize::Bit64),
vec![0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
);
assert_eq!(
Endianness::Big.bytes_of_ptr_sized(value, PointerSize::Bit64),
vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
);
}
#[test]
fn bytes_of_ptr_sized_bit128_depends_on_endianness() {
let value = 0x0102030405060708; let le = Endianness::Little.bytes_of_ptr_sized(value, PointerSize::Bit128);
let be = Endianness::Big.bytes_of_ptr_sized(value, PointerSize::Bit128);
assert_eq!(le[0..8], [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
assert_eq!(&le[8..16], &[0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(&be[0..8], &[0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(be[8..16], [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
}
#[test]
fn read_ptr_sized_round_trips_for_all_pointer_sizes() {
let test_vectors: Vec<(PointerSize, u64)> = vec![
(PointerSize::Bit8, 0xAB),
(PointerSize::Bit16, 0xABCD),
(PointerSize::Bit32, 0xDEAD_BEEF),
(PointerSize::Bit64, 0xDEAD_BEEF_CAFE_BABE),
(PointerSize::Bit128, 0xDEAD_BEEF_CAFE_BABE),
];
for endianness in [Endianness::Little, Endianness::Big] {
for (ptr_size, value) in &test_vectors {
let bytes = endianness.bytes_of_ptr_sized(*value, *ptr_size);
let restored = endianness.read_ptr_sized(&bytes, *ptr_size);
assert_eq!(
restored, *value,
"round-trip failed for {endianness:?} × {ptr_size:?}",
);
}
}
}
#[test]
fn le_and_be_differ_for_all_multi_byte_sizes() {
let value = 0x0102030405060708;
for ptr_size in [
PointerSize::Bit16,
PointerSize::Bit32,
PointerSize::Bit64,
PointerSize::Bit128,
] {
let le_bytes = Endianness::Little.bytes_of_ptr_sized(value, ptr_size);
let be_bytes = Endianness::Big.bytes_of_ptr_sized(value, ptr_size);
assert_ne!(
le_bytes, be_bytes,
"LE and BE should differ for {ptr_size:?}"
);
}
}
#[test]
fn le_and_be_agree_for_bit8() {
let le = Endianness::Little.bytes_of_ptr_sized(0xAB, PointerSize::Bit8);
let be = Endianness::Big.bytes_of_ptr_sized(0xAB, PointerSize::Bit8);
assert_eq!(le, be, "Bit8 should be identical regardless of endianness");
}
}