Optimize bincode serialized data so it matches real instr struct size

This commit is contained in:
wtfsck 2021-07-19 18:29:46 +02:00
parent 3b495e2a5a
commit bf77872c8b
5 changed files with 546 additions and 221 deletions

View File

@ -189,8 +189,9 @@ build_test_current_version() {
cargo test --color always --release --features serde
# Make sure the two read-mem methods behave the same
echo "==== TEST DEBUG: std decoder __internal_mem_vsib ===="
cargo test --color always --no-default-features --features "std decoder __internal_mem_vsib" --tests
# Also test serde code. It needs encoder to also test 'db x,y,z', see serde tests
echo "==== TEST DEBUG: std decoder encoder serde __internal_flip ===="
cargo test --color always --tests --no-default-features --features "std decoder encoder serde __internal_flip"
echo "==== TEST DEBUG ===="
cargo test --color always --tests --features serde

View File

@ -37,7 +37,7 @@ no_xop = []
no_d3now = []
serde = ["serde_crate"]
# Don't use
__internal_mem_vsib = []
__internal_flip = []
[dependencies]
# if: always

View File

@ -28,7 +28,7 @@ use core::{cmp, fmt, mem, ptr, u32};
use static_assertions::{const_assert, const_assert_eq};
#[rustfmt::skip]
#[cfg(any(feature = "__internal_mem_vsib", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[cfg(any(feature = "__internal_flip", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
static READ_OP_MEM_VSIB_FNS: [fn(&mut Decoder<'_>, &mut Instruction, Register, TupleType, bool) -> bool; 0x18] = [
decoder_read_op_mem_vsib_0,
decoder_read_op_mem_vsib_0,
@ -541,9 +541,9 @@ where
#[cfg(feature = "no_xop")]
handlers_xop: [(); 3],
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
read_op_mem_fns: [fn(&mut Decoder<'a>, &mut Instruction) -> bool; 0x18],
#[cfg(feature = "__internal_mem_vsib")]
#[cfg(feature = "__internal_flip")]
read_op_mem_fns: (),
state: State,
@ -940,7 +940,7 @@ impl<'a> Decoder<'a> {
mk_handlers_local!(handlers_xop_map10, "no_xop");
#[rustfmt::skip]
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
let read_op_mem_fns = [
Decoder::read_op_mem_0,
Decoder::read_op_mem_0,
@ -969,7 +969,7 @@ impl<'a> Decoder<'a> {
Decoder::read_op_mem_2,
Decoder::read_op_mem_2,
];
#[cfg(feature = "__internal_mem_vsib")]
#[cfg(feature = "__internal_flip")]
let read_op_mem_fns = ();
Ok(Decoder {
@ -1978,7 +1978,7 @@ impl<'a> Decoder<'a> {
}
#[must_use]
#[cfg(feature = "__internal_mem_vsib")]
#[cfg(feature = "__internal_flip")]
fn read_op_mem_32_or_64(&mut self, instruction: &mut Instruction) -> bool {
let base_reg = if self.state.address_size == OpSize::Size64 { Register::RAX } else { Register::EAX };
decoder_read_op_mem_32_or_64_vsib(self, instruction, base_reg, TupleType::N1, false)
@ -1987,7 +1987,7 @@ impl<'a> Decoder<'a> {
// Returns `true` if the SIB byte was read
// This is a specialized version of read_op_mem_32_or_64_vsib() which takes less arguments. Keep them in sync.
#[must_use]
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
#[inline(always)]
fn read_op_mem_32_or_64(&mut self, instruction: &mut Instruction) -> bool {
debug_assert!(self.state.address_size == OpSize::Size32 || self.state.address_size == OpSize::Size64);
@ -1998,7 +1998,7 @@ impl<'a> Decoder<'a> {
unsafe { (self.read_op_mem_fns.get_unchecked(index))(self, instruction) }
}
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
fn read_op_mem_1(&mut self, instruction: &mut Instruction) -> bool {
instruction_internal::internal_set_memory_displ_size(instruction, 1);
self.displ_index = self.data_ptr as u8;
@ -2014,7 +2014,7 @@ impl<'a> Decoder<'a> {
false
}
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
fn read_op_mem_1_4(&mut self, instruction: &mut Instruction) -> bool {
instruction_internal::internal_set_memory_displ_size(instruction, 1);
@ -2044,7 +2044,7 @@ impl<'a> Decoder<'a> {
true
}
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
fn read_op_mem_0(&mut self, instruction: &mut Instruction) -> bool {
let base_reg = if self.state.address_size == OpSize::Size64 { Register::RAX } else { Register::EAX };
write_base_reg!(instruction, self.state.extra_base_register_base + self.state.rm + base_reg as u32);
@ -2052,7 +2052,7 @@ impl<'a> Decoder<'a> {
false
}
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
fn read_op_mem_0_5(&mut self, instruction: &mut Instruction) -> bool {
self.displ_index = self.data_ptr as u8;
let d = self.read_u32();
@ -2075,7 +2075,7 @@ impl<'a> Decoder<'a> {
false
}
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
fn read_op_mem_2_4(&mut self, instruction: &mut Instruction) -> bool {
let sib = self.read_u8() as u32;
self.displ_index = self.data_ptr as u8;
@ -2105,7 +2105,7 @@ impl<'a> Decoder<'a> {
true
}
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
fn read_op_mem_2(&mut self, instruction: &mut Instruction) -> bool {
self.displ_index = self.data_ptr as u8;
let d = self.read_u32();
@ -2122,7 +2122,7 @@ impl<'a> Decoder<'a> {
false
}
#[cfg(not(feature = "__internal_mem_vsib"))]
#[cfg(not(feature = "__internal_flip"))]
fn read_op_mem_0_4(&mut self, instruction: &mut Instruction) -> bool {
let sib = self.read_u8() as u32;
const_assert_eq!(InstrScale::Scale1 as u32, 0);
@ -2316,7 +2316,7 @@ impl<'a> Decoder<'a> {
// Returns `true` if the SIB byte was read
// Same as read_op_mem_32_or_64() except it works with vsib memory operands. Keep them in sync.
#[must_use]
#[cfg(any(feature = "__internal_mem_vsib", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[cfg(any(feature = "__internal_flip", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[inline(always)]
fn decoder_read_op_mem_32_or_64_vsib(
this: &mut Decoder<'_>, instruction: &mut Instruction, index_reg: Register, tuple_type: TupleType, is_vsib: bool,
@ -2329,7 +2329,7 @@ fn decoder_read_op_mem_32_or_64_vsib(
unsafe { (READ_OP_MEM_VSIB_FNS.get_unchecked(index))(this, instruction, index_reg, tuple_type, is_vsib) }
}
#[cfg(any(feature = "__internal_mem_vsib", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[cfg(any(feature = "__internal_flip", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
fn decoder_read_op_mem_vsib_1(
this: &mut Decoder<'_>, instruction: &mut Instruction, _index_reg: Register, tuple_type: TupleType, _is_vsib: bool,
) -> bool {
@ -2347,7 +2347,7 @@ fn decoder_read_op_mem_vsib_1(
false
}
#[cfg(any(feature = "__internal_mem_vsib", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[cfg(any(feature = "__internal_flip", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
fn decoder_read_op_mem_vsib_1_4(
this: &mut Decoder<'_>, instruction: &mut Instruction, index_reg: Register, tuple_type: TupleType, is_vsib: bool,
) -> bool {
@ -2384,7 +2384,7 @@ fn decoder_read_op_mem_vsib_1_4(
true
}
#[cfg(any(feature = "__internal_mem_vsib", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[cfg(any(feature = "__internal_flip", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
fn decoder_read_op_mem_vsib_0(
this: &mut Decoder<'_>, instruction: &mut Instruction, _index_reg: Register, _tuple_type: TupleType, _is_vsib: bool,
) -> bool {
@ -2394,7 +2394,7 @@ fn decoder_read_op_mem_vsib_0(
false
}
#[cfg(any(feature = "__internal_mem_vsib", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[cfg(any(feature = "__internal_flip", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
fn decoder_read_op_mem_vsib_0_5(
this: &mut Decoder<'_>, instruction: &mut Instruction, _index_reg: Register, _tuple_type: TupleType, _is_vsib: bool,
) -> bool {
@ -2419,7 +2419,7 @@ fn decoder_read_op_mem_vsib_0_5(
false
}
#[cfg(any(feature = "__internal_mem_vsib", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[cfg(any(feature = "__internal_flip", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
fn decoder_read_op_mem_vsib_2_4(
this: &mut Decoder<'_>, instruction: &mut Instruction, index_reg: Register, _tuple_type: TupleType, is_vsib: bool,
) -> bool {
@ -2456,7 +2456,7 @@ fn decoder_read_op_mem_vsib_2_4(
true
}
#[cfg(any(feature = "__internal_mem_vsib", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[cfg(any(feature = "__internal_flip", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
fn decoder_read_op_mem_vsib_2(
this: &mut Decoder<'_>, instruction: &mut Instruction, _index_reg: Register, _tuple_type: TupleType, _is_vsib: bool,
) -> bool {
@ -2475,7 +2475,7 @@ fn decoder_read_op_mem_vsib_2(
false
}
#[cfg(any(feature = "__internal_mem_vsib", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
#[cfg(any(feature = "__internal_flip", not(feature = "no_evex"), not(feature = "no_vex"), not(feature = "no_xop")))]
fn decoder_read_op_mem_vsib_0_4(
this: &mut Decoder<'_>, instruction: &mut Instruction, index_reg: Register, _tuple_type: TupleType, is_vsib: bool,
) -> bool {

View File

@ -42,6 +42,8 @@ fn test_serde_json() {
fn test_bincode() {
test_serde(|instruction| {
let ser_bytes = bincode::serialize(instruction).unwrap();
#[cfg(not(feature = "__internal_flip"))]
assert_eq!(ser_bytes.len(), INSTRUCTION_TOTAL_SIZE);
bincode::deserialize(&ser_bytes).unwrap()
});
}

View File

@ -10542,6 +10542,10 @@ impl FusedIterator for OpKindIterator {}
// All the enums have generated ser/de implementations without using the proc-macro. This is the only one left
// that is needed to completely remove serde proc-macro.
//
// We've documented that we only support serializing and deserializing data created by the same version of iced.
// That means we can optimize bincode serialized instructions. It's wasting 35 bytes per serialized instruction
// because it stores each enum variant in a u32. That's almost a full instruction instance wasted with padding.
#[cfg(feature = "serde")]
const _: () = {
#[cfg(not(feature = "std"))]
@ -10556,101 +10560,100 @@ const _: () = {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "std")]
use std::collections::HashMap;
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
enum StructField {
next_rip,
flags1,
immediate,
mem_displ,
mem_displ_hi,
code,
mem_base_reg,
mem_index_reg,
regs,
op_kinds,
scale,
displ_size,
len,
db,
}
const NUM_STRUCT_FIELDS: usize = StructField::db as usize + 1;
const FIELDS: [&str; NUM_STRUCT_FIELDS] = [
"next_rip",
"flags1",
"immediate",
"mem_displ",
"mem_displ_hi",
"code",
"mem_base_reg",
"mem_index_reg",
"regs",
"op_kinds",
"scale",
"displ_size",
"len",
"db",
];
impl StructField {
#[inline]
fn values() -> impl Iterator<Item = StructField> + DoubleEndedIterator + ExactSizeIterator + FusedIterator {
// SAFETY: all values 0-max are valid enum values
(0..NUM_STRUCT_FIELDS).map(|x| unsafe { mem::transmute::<u8, StructField>(x as u8) })
}
}
lazy_static! {
static ref NAME_TO_FIELD: HashMap<&'static [u8], StructField> = FIELDS.iter().map(|&s| s.as_bytes()).zip(StructField::values()).collect();
}
impl TryFrom<usize> for StructField {
type Error = &'static str;
#[inline]
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < NUM_STRUCT_FIELDS {
// SAFETY: all values 0-max are valid enum values
Ok(unsafe { mem::transmute(value as u8) })
} else {
Err("Invalid struct field")
}
// eg. json
const NUM_FIELDS_READABLE: usize = 14;
// eg. bincode
const NUM_FIELDS_BINARY: usize = 20;
#[inline]
fn is_human_readable(value: bool) -> bool {
// This feature is used to test the other code paths
if cfg!(feature = "__internal_flip") {
value ^ true
} else {
value
}
}
impl Serialize for Instruction {
#[allow(clippy::missing_inline_in_public_items)]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut serde_state = serializer.serialize_struct("Instruction", NUM_STRUCT_FIELDS)?;
let mut fields = 0;
macro_rules! serialize {
($field:ident) => {
serde_state.serialize_field(stringify!($field), &self.$field)?;
fields += 1;
};
if is_human_readable(serializer.is_human_readable()) {
let mut serde_state = serializer.serialize_struct("Instruction", NUM_FIELDS_READABLE)?;
let mut fields = 0;
macro_rules! serialize {
($field:ident) => {
serde_state.serialize_field(stringify!($field), &self.$field)?;
fields += 1;
};
}
serialize!(next_rip);
serialize!(flags1);
serialize!(immediate);
serialize!(mem_displ);
serialize!(mem_displ_hi);
serialize!(code);
serialize!(mem_base_reg);
serialize!(mem_index_reg);
serialize!(regs);
serialize!(op_kinds);
serialize!(scale);
serialize!(displ_size);
serialize!(len);
serialize!(db);
debug_assert_eq!(fields, NUM_FIELDS_READABLE);
serde_state.end()
} else {
let mut serde_state = serializer.serialize_struct("Instruction", NUM_FIELDS_BINARY)?;
let mut fields = 0;
macro_rules! serialize {
($field:ident) => {
serde_state.serialize_field(stringify!($field), &self.$field)?;
fields += 1;
};
($field:ident, $underlying_ty:ty) => {
serde_state.serialize_field(stringify!($field), &(self.$field as $underlying_ty))?;
fields += 1;
};
($field:ident, $index:literal, $underlying_ty:ty) => {
serde_state.serialize_field(concat!(stringify!($field), stringify!($index)), &(self.$field[$index] as $underlying_ty))?;
fields += 1;
};
}
serialize!(next_rip);
serialize!(flags1);
serialize!(immediate);
serialize!(mem_displ);
serialize!(mem_displ_hi);
serialize!(code, CodeUnderlyingType);
serialize!(mem_base_reg, RegisterUnderlyingType);
serialize!(mem_index_reg, RegisterUnderlyingType);
debug_assert_eq!(self.regs.len(), 4);
serialize!(regs, 0, RegisterUnderlyingType);
serialize!(regs, 1, RegisterUnderlyingType);
serialize!(regs, 2, RegisterUnderlyingType);
serialize!(regs, 3, RegisterUnderlyingType);
debug_assert_eq!(self.op_kinds.len(), 4);
serialize!(op_kinds, 0, OpKindUnderlyingType);
serialize!(op_kinds, 1, OpKindUnderlyingType);
serialize!(op_kinds, 2, OpKindUnderlyingType);
serialize!(op_kinds, 3, OpKindUnderlyingType);
serialize!(scale, InstrScaleUnderlyingType);
serialize!(displ_size);
serialize!(len);
serialize!(db);
debug_assert_eq!(fields, NUM_FIELDS_BINARY);
serde_state.end()
}
serialize!(next_rip);
serialize!(flags1);
serialize!(immediate);
serialize!(mem_displ);
serialize!(mem_displ_hi);
serialize!(code);
serialize!(mem_base_reg);
serialize!(mem_index_reg);
serialize!(regs);
serialize!(op_kinds);
serialize!(scale);
serialize!(displ_size);
serialize!(len);
serialize!(db);
debug_assert_eq!(fields, NUM_STRUCT_FIELDS);
serde_state.end()
}
}
impl<'de> Deserialize<'de> for Instruction {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
macro_rules! mk_struct_field_visitor {
() => {
struct StructFieldVisitor;
impl<'de> de::Visitor<'de> for StructFieldVisitor {
type Value = StructField;
@ -10707,128 +10710,447 @@ const _: () = {
deserializer.deserialize_identifier(StructFieldVisitor)
}
}
struct Visitor<'de> {
marker: PhantomData<Instruction>,
lifetime: PhantomData<&'de ()>,
}
impl<'de> de::Visitor<'de> for Visitor<'de> {
type Value = Instruction;
#[inline]
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct Instruction")
};
}
impl<'de> Deserialize<'de> for Instruction {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if is_human_readable(deserializer.is_human_readable()) {
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
enum StructField {
next_rip,
flags1,
immediate,
mem_displ,
mem_displ_hi,
code,
mem_base_reg,
mem_index_reg,
regs,
op_kinds,
scale,
displ_size,
len,
db,
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::eval_order_dependence)]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut fields = 0;
macro_rules! next_element {
($field_name:ident : $field_ty:ty) => {{
fields += 1;
match seq.next_element::<$field_ty>()? {
Some(value) => value,
None => return Err(de::Error::invalid_length(fields, &"struct Instruction with all its fields")),
}
}};
const_assert_eq!(StructField::db as usize + 1, NUM_FIELDS_READABLE);
const FIELDS: [&str; NUM_FIELDS_READABLE] = [
"next_rip",
"flags1",
"immediate",
"mem_displ",
"mem_displ_hi",
"code",
"mem_base_reg",
"mem_index_reg",
"regs",
"op_kinds",
"scale",
"displ_size",
"len",
"db",
];
impl StructField {
#[inline]
fn values() -> impl Iterator<Item = StructField> + DoubleEndedIterator + ExactSizeIterator + FusedIterator {
// SAFETY: all values 0-max are valid enum values
(0..NUM_FIELDS_READABLE).map(|x| unsafe { mem::transmute::<u8, StructField>(x as u8) })
}
let instruction = Instruction {
next_rip: next_element!(next_rip: u64),
flags1: next_element!(flags1: u32),
immediate: next_element!(immediate: u32),
mem_displ: next_element!(mem_displ: u32),
mem_displ_hi: next_element!(mem_displ_hi: u32),
code: next_element!(code: Code),
mem_base_reg: next_element!(mem_base_reg: Register),
mem_index_reg: next_element!(mem_index_reg: Register),
regs: next_element!(regs: [Register; 4]),
op_kinds: next_element!(op_kinds: [OpKind; 4]),
scale: next_element!(scale: InstrScale),
displ_size: next_element!(displ_size: u8),
len: next_element!(len: u8),
db: next_element!(db: u8),
};
debug_assert_eq!(fields, NUM_STRUCT_FIELDS);
Ok(instruction)
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::eval_order_dependence)]
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut next_rip: Option<u64> = None;
let mut flags1: Option<u32> = None;
let mut immediate: Option<u32> = None;
let mut mem_displ: Option<u32> = None;
let mut mem_displ_hi: Option<u32> = None;
let mut code: Option<Code> = None;
let mut mem_base_reg: Option<Register> = None;
let mut mem_index_reg: Option<Register> = None;
let mut regs: Option<[Register; 4]> = None;
let mut op_kinds: Option<[OpKind; 4]> = None;
let mut scale: Option<InstrScale> = None;
let mut displ_size: Option<u8> = None;
let mut len: Option<u8> = None;
let mut db: Option<u8> = None;
while let Some(field) = map.next_key::<StructField>()? {
macro_rules! unpack {
($field:ident : $field_ty:ty) => {{
if $field.is_some() {
return Err(<A::Error as de::Error>::duplicate_field(stringify!($field)));
lazy_static! {
static ref NAME_TO_FIELD: HashMap<&'static [u8], StructField> =
FIELDS.iter().map(|&s| s.as_bytes()).zip(StructField::values()).collect();
}
impl TryFrom<usize> for StructField {
type Error = &'static str;
#[inline]
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < NUM_FIELDS_READABLE {
// SAFETY: all values 0-max are valid enum values
Ok(unsafe { mem::transmute(value as u8) })
} else {
Err("Invalid struct field")
}
}
}
mk_struct_field_visitor!();
struct Visitor<'de> {
marker: PhantomData<Instruction>,
lifetime: PhantomData<&'de ()>,
}
impl<'de> de::Visitor<'de> for Visitor<'de> {
type Value = Instruction;
#[inline]
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct Instruction")
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::eval_order_dependence)]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut fields = 0;
macro_rules! next_element {
($field_name:ident : $field_ty:ty) => {{
fields += 1;
match seq.next_element::<$field_ty>()? {
Some(value) => value,
None => return Err(de::Error::invalid_length(fields, &"struct Instruction with all its fields")),
}
$field = Some(map.next_value::<$field_ty>()?);
}};
}
match field {
StructField::next_rip => unpack!(next_rip: u64),
StructField::flags1 => unpack!(flags1: u32),
StructField::immediate => unpack!(immediate: u32),
StructField::mem_displ => unpack!(mem_displ: u32),
StructField::mem_displ_hi => unpack!(mem_displ_hi: u32),
StructField::code => unpack!(code: Code),
StructField::mem_base_reg => unpack!(mem_base_reg: Register),
StructField::mem_index_reg => unpack!(mem_index_reg: Register),
StructField::regs => unpack!(regs: [Register; 4]),
StructField::op_kinds => unpack!(op_kinds: [OpKind; 4]),
StructField::scale => unpack!(scale: InstrScale),
StructField::displ_size => unpack!(displ_size: u8),
StructField::len => unpack!(len: u8),
StructField::db => unpack!(db: u8),
let instruction = Instruction {
next_rip: next_element!(next_rip: u64),
flags1: next_element!(flags1: u32),
immediate: next_element!(immediate: u32),
mem_displ: next_element!(mem_displ: u32),
mem_displ_hi: next_element!(mem_displ_hi: u32),
code: next_element!(code: Code),
mem_base_reg: next_element!(mem_base_reg: Register),
mem_index_reg: next_element!(mem_index_reg: Register),
regs: next_element!(regs: [Register; 4]),
op_kinds: next_element!(op_kinds: [OpKind; 4]),
scale: next_element!(scale: InstrScale),
displ_size: next_element!(displ_size: u8),
len: next_element!(len: u8),
db: next_element!(db: u8),
};
debug_assert_eq!(fields, NUM_FIELDS_READABLE);
Ok(instruction)
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::eval_order_dependence)]
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut next_rip: Option<u64> = None;
let mut flags1: Option<u32> = None;
let mut immediate: Option<u32> = None;
let mut mem_displ: Option<u32> = None;
let mut mem_displ_hi: Option<u32> = None;
let mut code: Option<Code> = None;
let mut mem_base_reg: Option<Register> = None;
let mut mem_index_reg: Option<Register> = None;
let mut regs: Option<[Register; 4]> = None;
let mut op_kinds: Option<[OpKind; 4]> = None;
let mut scale: Option<InstrScale> = None;
let mut displ_size: Option<u8> = None;
let mut len: Option<u8> = None;
let mut db: Option<u8> = None;
while let Some(field) = map.next_key::<StructField>()? {
macro_rules! unpack {
($field:ident : $field_ty:ty) => {{
if $field.is_some() {
return Err(<A::Error as de::Error>::duplicate_field(stringify!($field)));
}
$field = Some(map.next_value::<$field_ty>()?);
}};
}
match field {
StructField::next_rip => unpack!(next_rip: u64),
StructField::flags1 => unpack!(flags1: u32),
StructField::immediate => unpack!(immediate: u32),
StructField::mem_displ => unpack!(mem_displ: u32),
StructField::mem_displ_hi => unpack!(mem_displ_hi: u32),
StructField::code => unpack!(code: Code),
StructField::mem_base_reg => unpack!(mem_base_reg: Register),
StructField::mem_index_reg => unpack!(mem_index_reg: Register),
StructField::regs => unpack!(regs: [Register; 4]),
StructField::op_kinds => unpack!(op_kinds: [OpKind; 4]),
StructField::scale => unpack!(scale: InstrScale),
StructField::displ_size => unpack!(displ_size: u8),
StructField::len => unpack!(len: u8),
StructField::db => unpack!(db: u8),
}
}
let mut fields = 0;
macro_rules! unpack_field {
($field:ident) => {{
fields += 1;
match $field {
Some(value) => value,
None => return Err(<A::Error as de::Error>::missing_field(stringify!($field))),
}
}};
}
let instruction = Instruction {
next_rip: unpack_field!(next_rip),
flags1: unpack_field!(flags1),
immediate: unpack_field!(immediate),
mem_displ: unpack_field!(mem_displ),
mem_displ_hi: unpack_field!(mem_displ_hi),
code: unpack_field!(code),
mem_base_reg: unpack_field!(mem_base_reg),
mem_index_reg: unpack_field!(mem_index_reg),
regs: unpack_field!(regs),
op_kinds: unpack_field!(op_kinds),
scale: unpack_field!(scale),
displ_size: unpack_field!(displ_size),
len: unpack_field!(len),
db: unpack_field!(db),
};
debug_assert_eq!(fields, NUM_FIELDS_READABLE);
Ok(instruction)
}
}
deserializer.deserialize_struct("Instruction", &FIELDS[..], Visitor { marker: PhantomData::<Instruction>, lifetime: PhantomData })
} else {
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
enum StructField {
next_rip,
flags1,
immediate,
mem_displ,
mem_displ_hi,
code,
mem_base_reg,
mem_index_reg,
regs0,
regs1,
regs2,
regs3,
op_kinds0,
op_kinds1,
op_kinds2,
op_kinds3,
scale,
displ_size,
len,
db,
}
const_assert_eq!(StructField::db as usize + 1, NUM_FIELDS_BINARY);
const FIELDS: [&str; NUM_FIELDS_BINARY] = [
"next_rip",
"flags1",
"immediate",
"mem_displ",
"mem_displ_hi",
"code",
"mem_base_reg",
"mem_index_reg",
"regs0",
"regs1",
"regs2",
"regs3",
"op_kinds0",
"op_kinds1",
"op_kinds2",
"op_kinds3",
"scale",
"displ_size",
"len",
"db",
];
impl StructField {
#[inline]
fn values() -> impl Iterator<Item = StructField> + DoubleEndedIterator + ExactSizeIterator + FusedIterator {
// SAFETY: all values 0-max are valid enum values
(0..NUM_FIELDS_BINARY).map(|x| unsafe { mem::transmute::<u8, StructField>(x as u8) })
}
}
lazy_static! {
static ref NAME_TO_FIELD: HashMap<&'static [u8], StructField> =
FIELDS.iter().map(|&s| s.as_bytes()).zip(StructField::values()).collect();
}
impl TryFrom<usize> for StructField {
type Error = &'static str;
#[inline]
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < NUM_FIELDS_BINARY {
// SAFETY: all values 0-max are valid enum values
Ok(unsafe { mem::transmute(value as u8) })
} else {
Err("Invalid struct field")
}
}
let mut fields = 0;
macro_rules! unpack_field {
($field:ident) => {{
fields += 1;
match $field {
Some(value) => value,
None => return Err(<A::Error as de::Error>::missing_field(stringify!($field))),
}
}};
}
let instruction = Instruction {
next_rip: unpack_field!(next_rip),
flags1: unpack_field!(flags1),
immediate: unpack_field!(immediate),
mem_displ: unpack_field!(mem_displ),
mem_displ_hi: unpack_field!(mem_displ_hi),
code: unpack_field!(code),
mem_base_reg: unpack_field!(mem_base_reg),
mem_index_reg: unpack_field!(mem_index_reg),
regs: unpack_field!(regs),
op_kinds: unpack_field!(op_kinds),
scale: unpack_field!(scale),
displ_size: unpack_field!(displ_size),
len: unpack_field!(len),
db: unpack_field!(db),
};
debug_assert_eq!(fields, NUM_STRUCT_FIELDS);
Ok(instruction)
}
mk_struct_field_visitor!();
struct Visitor<'de> {
marker: PhantomData<Instruction>,
lifetime: PhantomData<&'de ()>,
}
impl<'de> de::Visitor<'de> for Visitor<'de> {
type Value = Instruction;
#[inline]
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct Instruction")
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::eval_order_dependence)]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut fields = 0;
macro_rules! next_element {
($field_name:ident : $field_ty:ty) => {{
fields += 1;
match seq.next_element::<$field_ty>()? {
Some(value) => value,
None => return Err(de::Error::invalid_length(fields, &"struct Instruction with all its fields")),
}
}};
($field_name:ident : $field_ty:ty : $underlying_ty:ty) => {{
fields += 1;
let value = match seq.next_element::<$underlying_ty>()? {
Some(value) => value,
None => return Err(de::Error::invalid_length(fields, &"struct Instruction with all its fields")),
};
if let Ok(enum_value) = <$field_ty as TryFrom<usize>>::try_from(value as usize) {
enum_value
} else {
return Err(<A::Error as de::Error>::invalid_value(
de::Unexpected::Unsigned(value.into()),
&"an enum variant in range",
));
}
}};
}
let instruction = Instruction {
next_rip: next_element!(next_rip: u64),
flags1: next_element!(flags1: u32),
immediate: next_element!(immediate: u32),
mem_displ: next_element!(mem_displ: u32),
mem_displ_hi: next_element!(mem_displ_hi: u32),
code: next_element!(code: Code: CodeUnderlyingType),
mem_base_reg: next_element!(mem_base_reg: Register: RegisterUnderlyingType),
mem_index_reg: next_element!(mem_index_reg: Register: RegisterUnderlyingType),
regs: [
next_element!(regs0: Register: RegisterUnderlyingType),
next_element!(regs1: Register: RegisterUnderlyingType),
next_element!(regs2: Register: RegisterUnderlyingType),
next_element!(regs3: Register: RegisterUnderlyingType),
],
op_kinds: [
next_element!(op_kinds0: OpKind: OpKindUnderlyingType),
next_element!(op_kinds1: OpKind: OpKindUnderlyingType),
next_element!(op_kinds2: OpKind: OpKindUnderlyingType),
next_element!(op_kinds3: OpKind: OpKindUnderlyingType),
],
scale: next_element!(scale: InstrScale: InstrScaleUnderlyingType),
displ_size: next_element!(displ_size: u8),
len: next_element!(len: u8),
db: next_element!(db: u8),
};
debug_assert_eq!(fields, NUM_FIELDS_BINARY);
Ok(instruction)
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::eval_order_dependence)]
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut next_rip: Option<u64> = None;
let mut flags1: Option<u32> = None;
let mut immediate: Option<u32> = None;
let mut mem_displ: Option<u32> = None;
let mut mem_displ_hi: Option<u32> = None;
let mut code: Option<Code> = None;
let mut mem_base_reg: Option<Register> = None;
let mut mem_index_reg: Option<Register> = None;
let mut regs0: Option<Register> = None;
let mut regs1: Option<Register> = None;
let mut regs2: Option<Register> = None;
let mut regs3: Option<Register> = None;
let mut op_kinds0: Option<OpKind> = None;
let mut op_kinds1: Option<OpKind> = None;
let mut op_kinds2: Option<OpKind> = None;
let mut op_kinds3: Option<OpKind> = None;
let mut scale: Option<InstrScale> = None;
let mut displ_size: Option<u8> = None;
let mut len: Option<u8> = None;
let mut db: Option<u8> = None;
while let Some(field) = map.next_key::<StructField>()? {
macro_rules! unpack {
($field:ident : $field_ty:ty) => {{
if $field.is_some() {
return Err(<A::Error as de::Error>::duplicate_field(stringify!($field)));
}
$field = Some(map.next_value::<$field_ty>()?);
}};
($field:ident : $field_ty:ty : $underlying_ty:ty) => {{
if $field.is_some() {
return Err(<A::Error as de::Error>::duplicate_field(stringify!($field)));
}
let value = map.next_value::<$underlying_ty>()?;
if let Ok(enum_value) = <$field_ty as TryFrom<usize>>::try_from(value as usize) {
$field = Some(enum_value);
} else {
return Err(<A::Error as de::Error>::invalid_value(
de::Unexpected::Unsigned(value.into()),
&"an enum variant in range",
));
}
}};
}
match field {
StructField::next_rip => unpack!(next_rip: u64),
StructField::flags1 => unpack!(flags1: u32),
StructField::immediate => unpack!(immediate: u32),
StructField::mem_displ => unpack!(mem_displ: u32),
StructField::mem_displ_hi => unpack!(mem_displ_hi: u32),
StructField::code => unpack!(code: Code: CodeUnderlyingType),
StructField::mem_base_reg => unpack!(mem_base_reg: Register: RegisterUnderlyingType),
StructField::mem_index_reg => unpack!(mem_index_reg: Register: RegisterUnderlyingType),
StructField::regs0 => unpack!(regs0: Register: RegisterUnderlyingType),
StructField::regs1 => unpack!(regs1: Register: RegisterUnderlyingType),
StructField::regs2 => unpack!(regs2: Register: RegisterUnderlyingType),
StructField::regs3 => unpack!(regs3: Register: RegisterUnderlyingType),
StructField::op_kinds0 => unpack!(op_kinds0: OpKind: OpKindUnderlyingType),
StructField::op_kinds1 => unpack!(op_kinds1: OpKind: OpKindUnderlyingType),
StructField::op_kinds2 => unpack!(op_kinds2: OpKind: OpKindUnderlyingType),
StructField::op_kinds3 => unpack!(op_kinds3: OpKind: OpKindUnderlyingType),
StructField::scale => unpack!(scale: InstrScale: InstrScaleUnderlyingType),
StructField::displ_size => unpack!(displ_size: u8),
StructField::len => unpack!(len: u8),
StructField::db => unpack!(db: u8),
}
}
let mut fields = 0;
macro_rules! unpack_field {
($field:ident) => {{
fields += 1;
match $field {
Some(value) => value,
None => return Err(<A::Error as de::Error>::missing_field(stringify!($field))),
}
}};
}
let instruction = Instruction {
next_rip: unpack_field!(next_rip),
flags1: unpack_field!(flags1),
immediate: unpack_field!(immediate),
mem_displ: unpack_field!(mem_displ),
mem_displ_hi: unpack_field!(mem_displ_hi),
code: unpack_field!(code),
mem_base_reg: unpack_field!(mem_base_reg),
mem_index_reg: unpack_field!(mem_index_reg),
regs: [unpack_field!(regs0), unpack_field!(regs1), unpack_field!(regs2), unpack_field!(regs3)],
op_kinds: [unpack_field!(op_kinds0), unpack_field!(op_kinds1), unpack_field!(op_kinds2), unpack_field!(op_kinds3)],
scale: unpack_field!(scale),
displ_size: unpack_field!(displ_size),
len: unpack_field!(len),
db: unpack_field!(db),
};
debug_assert_eq!(fields, NUM_FIELDS_BINARY);
Ok(instruction)
}
}
deserializer.deserialize_struct("Instruction", &FIELDS[..], Visitor { marker: PhantomData::<Instruction>, lifetime: PhantomData })
}
deserializer.deserialize_struct("Instruction", &FIELDS[..], Visitor { marker: PhantomData::<Instruction>, lifetime: PhantomData })
}
}
};