idakit-sys 0.2.0

Raw FFI bindings to IDA Pro's idalib runtime and the idakit C++ facade
Documentation
//! Instruction-decode sentinels shared with the generated decode bridge: control-flow flags and
//! operand access bits. The scalar sentinels (`MAX_OPS`, `OP_*`, `REG_NONE`, `FLOW_*`) are
//! generated by `idakit-sys-codegen`'s `instruction` domain spec.

use bitflags::bitflags;

bitflags! {
    /// `InstructionData::flow` control-flow bits for a decoded instruction: [`crate::FLOW_CALL`]/
    /// [`crate::FLOW_RET`]/[`crate::FLOW_JUMP`]/[`crate::FLOW_INDIRECT`]/[`crate::FLOW_STOPS`].
    ///
    /// `FLOW_*` is idakit's own facade sentinel, not a raw SDK value; accepts any bit pattern
    /// (`from_bits_retain`), since `InstructionData::flow` is a raw `u8` field the C++ decoder
    /// writes.
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
    pub struct FlowFlags: u8 {
        /// The instruction is a call.
        const CALL = crate::FLOW_CALL;
        /// The instruction returns.
        const RET = crate::FLOW_RET;
        /// The instruction jumps.
        const JUMP = crate::FLOW_JUMP;
        /// The transfer target is indirect, not statically known.
        const INDIRECT = crate::FLOW_INDIRECT;
        /// Sequential flow stops after this instruction.
        const STOPS = crate::FLOW_STOPS;
    }
}

bitflags! {
    /// `OperandData::access` bits for a decoded operand: `bit0` read, `bit1` written.
    ///
    /// Accepts any bit pattern (`from_bits_retain`), since `OperandData::access` is a raw `u8`
    /// field the C++ decoder writes.
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
    pub struct OperandAccess: u8 {
        /// The operand is read.
        const READ = 1;
        /// The operand is written.
        const WRITTEN = 2;
    }
}

#[cfg(test)]
mod tests {
    use assert2::assert;
    use proptest::prelude::*;
    use rstest::rstest;

    use super::*;

    #[rstest]
    #[case::call(FlowFlags::CALL, crate::FLOW_CALL)]
    #[case::ret(FlowFlags::RET, crate::FLOW_RET)]
    #[case::jump(FlowFlags::JUMP, crate::FLOW_JUMP)]
    #[case::indirect(FlowFlags::INDIRECT, crate::FLOW_INDIRECT)]
    #[case::stops(FlowFlags::STOPS, crate::FLOW_STOPS)]
    fn flags_pin_the_generated_facade_values(#[case] flag: FlowFlags, #[case] raw: u8) {
        assert!(flag.bits() == raw);
    }

    #[rstest]
    #[case::read(OperandAccess::READ, 1)]
    #[case::written(OperandAccess::WRITTEN, 2)]
    fn operand_access_flags_pin_the_raw_values(#[case] flag: OperandAccess, #[case] raw: u8) {
        assert!(flag.bits() == raw);
    }

    proptest! {
        #[test]
        fn flow_flags_from_bits_retain_round_trips_every_byte(raw: u8) {
            prop_assert_eq!(FlowFlags::from_bits_retain(raw).bits(), raw);
        }

        #[test]
        fn flow_flags_union_and_intersection_are_raw_bitwise_ops(a: u8, b: u8) {
            let (fa, fb) = (FlowFlags::from_bits_retain(a), FlowFlags::from_bits_retain(b));
            prop_assert_eq!((fa | fb).bits(), a | b);
            prop_assert_eq!((fa & fb).bits(), a & b);
        }

        #[test]
        fn operand_access_from_bits_retain_round_trips_every_byte(raw: u8) {
            prop_assert_eq!(OperandAccess::from_bits_retain(raw).bits(), raw);
        }

        #[test]
        fn operand_access_union_and_intersection_are_raw_bitwise_ops(a: u8, b: u8) {
            let (fa, fb) = (OperandAccess::from_bits_retain(a), OperandAccess::from_bits_retain(b));
            prop_assert_eq!((fa | fb).bits(), a | b);
            prop_assert_eq!((fa & fb).bits(), a & b);
        }
    }
}