use std::fmt::Debug;
use std::hash::Hash;
use crate::ir::value::ConstValue;
use crate::PointerSize;
#[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 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");
}
}