use crate::wire::error::WireError;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
pub const SCOPE_ALL: u8 = 0;
pub const SCOPE_BY_ACCOUNT: u8 = 1;
pub const SCOPE_BY_SIDE: u8 = 2;
#[derive(
Debug, Clone, Copy, PartialEq, Eq, FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout,
)]
#[repr(C, packed)]
pub struct MassCancelWire {
pub client_ts: u64,
pub account_id: u64,
pub scope: u8,
pub _pad: [u8; 7],
}
const _: () = assert!(core::mem::size_of::<MassCancelWire>() == 24);
impl MassCancelWire {
#[must_use]
#[inline]
pub fn as_payload_bytes(&self) -> &[u8] {
<Self as zerocopy::IntoBytes>::as_bytes(self)
}
}
#[inline]
pub fn decode_mass_cancel(payload: &[u8]) -> Result<MassCancelWire, WireError> {
let view = MassCancelWire::ref_from_bytes(payload)
.map_err(|_| WireError::InvalidPayload("MassCancel: payload size mismatch"))?;
let scope = { view.scope };
let pad = { view._pad };
match scope {
SCOPE_ALL | SCOPE_BY_ACCOUNT => {
if pad.iter().any(|&byte| byte != 0) {
return Err(WireError::InvalidPayload(
"MassCancel: non-zero reserved padding",
));
}
}
SCOPE_BY_SIDE => {
let head = *pad.first().ok_or(WireError::Truncated)?;
if head & !1 != 0 {
return Err(WireError::InvalidPayload(
"MassCancel: reserved bits set in BySide pad[0]",
));
}
if pad.iter().skip(1).any(|&byte| byte != 0) {
return Err(WireError::InvalidPayload(
"MassCancel: non-zero reserved padding",
));
}
}
_ => return Err(WireError::InvalidPayload("MassCancel: unknown scope")),
}
Ok(*view)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wire::framing::{decode_frame, encode_frame};
use proptest::prelude::*;
use zerocopy::IntoBytes;
proptest! {
#[test]
fn roundtrip_through_frame(
client_ts in any::<u64>(),
account_id in any::<u64>(),
scope in 0u8..=2u8,
side_bit in 0u8..=1u8,
) {
let mut pad = [0u8; 7];
if scope == SCOPE_BY_SIDE
&& let Some(slot) = pad.get_mut(0)
{
*slot = side_bit;
}
let original = MassCancelWire {
client_ts,
account_id,
scope,
_pad: pad,
};
let mut framed = Vec::new();
encode_frame(0x04, original.as_bytes(), &mut framed).expect("encode_frame");
let (kind, payload, _) = decode_frame(&framed).expect("decode_frame");
prop_assert_eq!(kind, 0x04u8);
let decoded = decode_mass_cancel(payload).expect("decode_mass_cancel");
prop_assert_eq!({ decoded.client_ts }, client_ts);
prop_assert_eq!({ decoded.account_id }, account_id);
prop_assert_eq!({ decoded.scope }, scope);
prop_assert_eq!({ decoded._pad }, pad);
}
}
#[test]
fn rejects_unknown_scope() {
let bad = MassCancelWire {
client_ts: 0,
account_id: 0,
scope: 9,
_pad: [0u8; 7],
};
let bytes = bad.as_bytes();
assert!(matches!(
decode_mass_cancel(bytes),
Err(WireError::InvalidPayload(_))
));
}
#[test]
fn rejects_wrong_size() {
let buf = [0u8; 23];
assert!(matches!(
decode_mass_cancel(&buf),
Err(WireError::InvalidPayload(_))
));
}
}