diff --git a/build/build-rust b/build/build-rust index f347dde1d..0efaf521f 100755 --- a/build/build-rust +++ b/build/build-rust @@ -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 diff --git a/src/rust/iced-x86/Cargo.toml b/src/rust/iced-x86/Cargo.toml index e80c01ef4..97ab4c42b 100644 --- a/src/rust/iced-x86/Cargo.toml +++ b/src/rust/iced-x86/Cargo.toml @@ -37,7 +37,7 @@ no_xop = [] no_d3now = [] serde = ["serde_crate"] # Don't use -__internal_mem_vsib = [] +__internal_flip = [] [dependencies] # if: always diff --git a/src/rust/iced-x86/src/decoder.rs b/src/rust/iced-x86/src/decoder.rs index c6b47376c..0e950dd5b 100644 --- a/src/rust/iced-x86/src/decoder.rs +++ b/src/rust/iced-x86/src/decoder.rs @@ -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 { diff --git a/src/rust/iced-x86/src/decoder/tests/serde_tests.rs b/src/rust/iced-x86/src/decoder/tests/serde_tests.rs index 02f31ed8b..2fecf2325 100644 --- a/src/rust/iced-x86/src/decoder/tests/serde_tests.rs +++ b/src/rust/iced-x86/src/decoder/tests/serde_tests.rs @@ -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() }); } diff --git a/src/rust/iced-x86/src/instruction.rs b/src/rust/iced-x86/src/instruction.rs index 236748621..bb4192289 100644 --- a/src/rust/iced-x86/src/instruction.rs +++ b/src/rust/iced-x86/src/instruction.rs @@ -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 + DoubleEndedIterator + ExactSizeIterator + FusedIterator { - // SAFETY: all values 0-max are valid enum values - (0..NUM_STRUCT_FIELDS).map(|x| unsafe { mem::transmute::(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 for StructField { - type Error = &'static str; - #[inline] - fn try_from(value: usize) -> Result { - 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(&self, serializer: S) -> Result 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(deserializer: D) -> Result - 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, - 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(deserializer: D) -> Result + 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(self, mut seq: A) -> Result - 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 + DoubleEndedIterator + ExactSizeIterator + FusedIterator { + // SAFETY: all values 0-max are valid enum values + (0..NUM_FIELDS_READABLE).map(|x| unsafe { mem::transmute::(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(self, mut map: A) -> Result - where - A: de::MapAccess<'de>, - { - let mut next_rip: Option = None; - let mut flags1: Option = None; - let mut immediate: Option = None; - let mut mem_displ: Option = None; - let mut mem_displ_hi: Option = None; - let mut code: Option = None; - let mut mem_base_reg: Option = None; - let mut mem_index_reg: Option = None; - let mut regs: Option<[Register; 4]> = None; - let mut op_kinds: Option<[OpKind; 4]> = None; - let mut scale: Option = None; - let mut displ_size: Option = None; - let mut len: Option = None; - let mut db: Option = None; - while let Some(field) = map.next_key::()? { - macro_rules! unpack { - ($field:ident : $field_ty:ty) => {{ - if $field.is_some() { - return Err(::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 for StructField { + type Error = &'static str; + #[inline] + fn try_from(value: usize) -> Result { + 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, + 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(self, mut seq: A) -> Result + 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(self, mut map: A) -> Result + where + A: de::MapAccess<'de>, + { + let mut next_rip: Option = None; + let mut flags1: Option = None; + let mut immediate: Option = None; + let mut mem_displ: Option = None; + let mut mem_displ_hi: Option = None; + let mut code: Option = None; + let mut mem_base_reg: Option = None; + let mut mem_index_reg: Option = None; + let mut regs: Option<[Register; 4]> = None; + let mut op_kinds: Option<[OpKind; 4]> = None; + let mut scale: Option = None; + let mut displ_size: Option = None; + let mut len: Option = None; + let mut db: Option = None; + while let Some(field) = map.next_key::()? { + macro_rules! unpack { + ($field:ident : $field_ty:ty) => {{ + if $field.is_some() { + return Err(::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(::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::, 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 + DoubleEndedIterator + ExactSizeIterator + FusedIterator { + // SAFETY: all values 0-max are valid enum values + (0..NUM_FIELDS_BINARY).map(|x| unsafe { mem::transmute::(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 for StructField { + type Error = &'static str; + #[inline] + fn try_from(value: usize) -> Result { + 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(::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, + 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(self, mut seq: A) -> Result + 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>::try_from(value as usize) { + enum_value + } else { + return Err(::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(self, mut map: A) -> Result + where + A: de::MapAccess<'de>, + { + let mut next_rip: Option = None; + let mut flags1: Option = None; + let mut immediate: Option = None; + let mut mem_displ: Option = None; + let mut mem_displ_hi: Option = None; + let mut code: Option = None; + let mut mem_base_reg: Option = None; + let mut mem_index_reg: Option = None; + let mut regs0: Option = None; + let mut regs1: Option = None; + let mut regs2: Option = None; + let mut regs3: Option = None; + let mut op_kinds0: Option = None; + let mut op_kinds1: Option = None; + let mut op_kinds2: Option = None; + let mut op_kinds3: Option = None; + let mut scale: Option = None; + let mut displ_size: Option = None; + let mut len: Option = None; + let mut db: Option = None; + while let Some(field) = map.next_key::()? { + macro_rules! unpack { + ($field:ident : $field_ty:ty) => {{ + if $field.is_some() { + return Err(::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(::duplicate_field(stringify!($field))); + } + let value = map.next_value::<$underlying_ty>()?; + if let Ok(enum_value) = <$field_ty as TryFrom>::try_from(value as usize) { + $field = Some(enum_value); + } else { + return Err(::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(::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::, lifetime: PhantomData }) } - deserializer.deserialize_struct("Instruction", &FIELDS[..], Visitor { marker: PhantomData::, lifetime: PhantomData }) } } };