use bnb::{BitEnum, bitfield, u3, u4, u5};
#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq, Default)]
#[bit_enum(u4)]
enum Op {
#[default]
Query = 0,
Inverse = 1,
Status = 2,
Update = 5,
R3 = 3,
R4 = 4,
R6 = 6,
R7 = 7,
R8 = 8,
R9 = 9,
R10 = 10,
R11 = 11,
R12 = 12,
R13 = 13,
R14 = 14,
R15 = 15,
}
#[bitfield(u8, bits = msb)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct OpCode {
response: bool,
op: Op,
}
#[bitfield(u8, bits = msb)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Flags {
authoritative: bool,
truncated: bool,
recursion_desired: bool,
recursion_available: bool,
reserved: u3,
}
#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
#[bit_enum(u4)]
enum RCode {
NoError,
FormErr,
ServFail,
NxDomain,
#[catch_all]
Other(u4), }
#[bitfield(u16, bits = msb, bytes = be)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct State {
opcode: OpCode,
flags: Flags,
rcode: RCode,
}
#[test]
fn dns_state_bit_positions_match_the_wire() {
let state = State::new()
.with_opcode(OpCode::new().with_op(Op::Status))
.with_rcode(RCode::ServFail);
assert_eq!(state.raw(), 0x1002);
assert_eq!(state.to_be_bytes(), [0x10, 0x02]);
assert_eq!(state.opcode().op(), Op::Status);
assert!(!state.opcode().response());
assert_eq!(state.rcode(), RCode::ServFail);
}
#[test]
fn dns_state_nested_flags_land_in_the_right_bits() {
let state = State::new().with_flags(Flags::new().with_authoritative(true));
assert_eq!(state.raw(), 1 << 10);
assert!(state.flags().authoritative());
let state = State::new().with_flags(Flags::new().with_recursion_desired(true));
assert_eq!(state.raw(), 1 << 8);
}
#[test]
fn dns_rcode_catch_all_preserves_unknown_values() {
let state = State::from_raw(0x000F);
assert_eq!(state.rcode(), RCode::Other(u4::new(0xF)));
assert_eq!(state.with_rcode(RCode::Other(u4::new(0xF))).raw(), 0x000F);
}
#[test]
fn dns_op_is_exhaustive_and_round_trips_all_16() {
for v in 0u128..16 {
let op = <Op as bnb::Bits>::from_bits(v);
assert_eq!(<Op as bnb::Bits>::into_bits(op), v, "op {v}");
}
}
#[bitfield(u8, bits = lsb)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct SecurityMode {
user_mode: bool,
encrypt_passwords: bool,
signing_enabled: bool,
signing_required: bool,
reserved: u4,
}
#[bitfield(u32, bits = lsb, bytes = le)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Capabilities {
raw_mode: bool,
mpx_mode: bool,
unicode: bool,
large_files: bool,
rest: bnb::u28,
}
#[test]
fn smb_security_mode_lsb_first() {
let mode = SecurityMode::new()
.with_user_mode(true)
.with_signing_required(true);
assert_eq!(mode.raw(), 0b0000_1001);
assert!(mode.user_mode());
assert!(mode.signing_required());
assert!(!mode.signing_enabled());
}
#[test]
fn smb_capabilities_little_endian_bytes() {
let caps = Capabilities::new().with_unicode(true); assert_eq!(caps.raw(), 0x0000_0004);
assert_eq!(caps.to_le_bytes(), [0x04, 0x00, 0x00, 0x00]);
assert_eq!(caps.to_be_bytes(), [0x00, 0x00, 0x00, 0x04]);
}
#[bitfield(u16, bytes = be)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct ManualState {
#[bits(11..=15)]
opcode: u5,
#[bits(4..=10)]
flags: bnb::u7,
#[bits(0..=3)]
rcode: u4,
}
#[test]
fn manual_ranges_give_absolute_offsets() {
let s = ManualState::new()
.with_opcode(u5::new(2))
.with_rcode(u4::new(2));
assert_eq!(s.raw(), 0x1002); assert_eq!(s.opcode(), u5::new(2));
}
#[test]
fn bitfield_seam_exposes_layout_metadata() {
use bnb::{BitOrder, Bitfield, ByteOrder};
assert_eq!(<State as Bitfield>::WIDTH, 16);
assert_eq!(<State as Bitfield>::BYTE_ORDER, ByteOrder::Big);
assert_eq!(<State as Bitfield>::BIT_ORDER, BitOrder::Msb);
assert_eq!(<SecurityMode as Bitfield>::BIT_ORDER, BitOrder::Lsb);
assert_eq!(<Capabilities as Bitfield>::BYTE_ORDER, ByteOrder::Little);
let s = State::from_raw(0x1002);
assert_eq!(Bitfield::to_raw(s), 0x1002u16);
assert_eq!(<State as Bitfield>::from_raw(0x1002), s);
}
#[test]
fn nested_bitfield_implements_bits_for_composition() {
assert_eq!(<OpCode as bnb::Bits>::BITS, 5);
assert_eq!(<Flags as bnb::Bits>::BITS, 7);
let op = OpCode::new().with_response(true).with_op(Op::Update);
assert_eq!(op.raw(), 0b1_0101);
assert_eq!(bnb::Bits::into_bits(op), 0b1_0101);
}