#![allow(clippy::assign_op_pattern)]
use bebytes::BeBytes;
fn main() {
println!("\n=== BeBytes Serialization Test Suite ===");
println!("\nShowing side-by-side comparison of original vs deserialized values\n");
demo_endianness();
demo_bit_fields();
demo_multi_byte_types();
demo_enum_serialization();
demo_enum_bit_packing();
demo_flag_enums();
demo_options();
demo_vectors();
demo_nested_structs();
demo_complete_functionality();
demo_nested_field_access();
demo_char_support();
demo_string_support();
demo_size_expressions();
println!("\n=== All tests completed ===");
}
fn print_section(title: &str) {
println!("\n--- {title} ---");
}
fn print_bytes(bytes: &[u8]) -> String {
let hex: Vec<String> = bytes.iter().map(|b| format!("0x{b:02X}")).collect();
format!("[{}] ({} bytes)", hex.join(", "), bytes.len())
}
fn compare_values<T: std::fmt::Debug + PartialEq>(
name: &str,
original: &T,
decoded: &T,
bytes: &[u8],
) {
let original_str = format!("{original:#?}");
let decoded_str = format!("{decoded:#?}");
println!("\n{name} Comparison:");
println!("Serialized: {}", print_bytes(bytes));
println!("\nORIGINAL | DECODED");
println!("---------------------------------+---------------------------------");
let orig_lines: Vec<&str> = original_str.lines().collect();
let dec_lines: Vec<&str> = decoded_str.lines().collect();
let max_lines = orig_lines.len().max(dec_lines.len());
for i in 0..max_lines {
let orig = orig_lines.get(i).unwrap_or(&"");
let dec = dec_lines.get(i).unwrap_or(&"");
println!("{orig:<33} | {dec:<33}");
}
assert_eq!(
original, decoded,
"Serialization/deserialization mismatch for {name}"
);
}
fn compare_enum_values<T>(name: &str, original: T, decoded: T)
where
T: std::fmt::Debug + PartialEq + Copy + BeBytes,
{
let bytes = original.to_be_bytes();
println!("\n{name} Comparison:");
println!("Serialized: {}", print_bytes(&bytes));
println!("\nORIGINAL: {original:?}");
println!("DECODED: {decoded:?}");
assert_eq!(
original, decoded,
"Enum serialization/deserialization mismatch for {name}"
);
}
fn demo_endianness() {
print_section("1. ENDIANNESS DEMONSTRATION");
let be_original = U16 {
first: 1,
second: 16383,
fourth: 0,
};
println!("\nBig-Endian test:");
let be_bytes = be_original.to_be_bytes();
println!("BE bytes: {}", print_bytes(&be_bytes));
let (decoded_be, _) = U16::try_from_be_bytes(&be_bytes).unwrap();
compare_values("U16 (BE)", &be_original, &decoded_be, &be_bytes);
let le_original = U16 {
first: 0, second: 16383, fourth: 1, };
println!("\nLittle-Endian test:");
let le_bytes = le_original.to_le_bytes();
println!("LE bytes: {}", print_bytes(&le_bytes));
let (decoded_from_le, _) = U16::try_from_le_bytes(&le_bytes).unwrap();
compare_values("U16 (LE)", &le_original, &decoded_from_le, &le_bytes);
println!("\nNote: BE and LE bit field ordering produces different logical values");
println!("This is expected behavior - bit fields have endianness-dependent semantics");
}
fn demo_bit_fields() {
print_section("2. BIT FIELD DEMONSTRATION");
let original = U8 {
first: 1, second: 2, third: 3, fourth: 4, };
let bytes = original.to_be_bytes();
let (decoded, _) = U8::try_from_be_bytes(&bytes).unwrap();
compare_values("U8", &original, &decoded, &bytes);
println!("\nBit layout breakdown:");
println!(
"first (1 bit): {:01b} = {}",
original.first, original.first
);
println!(
"second (3 bits): {:03b} = {}",
original.second, original.second
);
println!(
"third (4 bits): {:04b} = {}",
original.third, original.third
);
println!(
"fourth (8 bits): {:08b} = {}",
original.fourth, original.fourth
);
let error = ErrorEstimateMini {
s_bit: 1,
z_bit: 0,
scale: 63, multiplier: 3,
};
let bytes = error.to_be_bytes();
let (decoded, _) = ErrorEstimateMini::try_from_be_bytes(&bytes).unwrap();
compare_values("ErrorEstimateMini", &error, &decoded, &bytes);
}
fn demo_multi_byte_types() {
print_section("3. MULTI-BYTE TYPE DEMONSTRATION");
let u16_val = U16 {
first: 1,
second: 16383,
fourth: 0,
};
let bytes = u16_val.to_be_bytes();
let (decoded, _) = U16::try_from_be_bytes(&bytes).unwrap();
compare_values("U16", &u16_val, &decoded, &bytes);
let u32_val = U32 {
first: 1,
second: 32383,
fourth: 1,
};
let bytes = u32_val.to_be_bytes();
let (decoded, _) = U32::try_from_be_bytes(&bytes).unwrap();
compare_values("U32", &u32_val, &decoded, &bytes);
}
fn demo_enum_serialization() {
print_section("4. ENUM SERIALIZATION");
println!("\nEnum discriminant mapping:");
println!("SetupResponse = 0");
println!("ServerStart = 1");
println!("SetupRequest = 2");
let original = DummyEnum::ServerStart;
let bytes = original.to_be_bytes();
let (decoded, _) = DummyEnum::try_from_be_bytes(&bytes).unwrap();
compare_enum_values("DummyEnum", original, decoded);
println!("\nAll variants test:");
for variant in &[
DummyEnum::SetupResponse,
DummyEnum::ServerStart,
DummyEnum::SetupRequest,
] {
let bytes = variant.to_be_bytes();
let (decoded, _) = DummyEnum::try_from_be_bytes(&bytes).unwrap();
println!(
"{:?} -> {} -> {:?} ({})",
variant,
print_bytes(&bytes),
decoded,
if variant == &decoded { "✓" } else { "✗" }
);
}
}
fn demo_enum_bit_packing() {
print_section("5. ENUM BIT PACKING (Auto-sized)");
println!("\nBits used:");
println!("Status (4 variants) = 2 bits");
println!("Priority (3 variants) = 2 bits");
println!("LargeEnum (17 variants) = 5 bits");
let original = PacketHeader {
version: 15, status: 1, priority: 2, };
let bytes = original.to_be_bytes();
let (decoded, _) = PacketHeader::try_from_be_bytes(&bytes).unwrap();
println!("\nPacketHeader Comparison:");
println!("Serialized: {}", print_bytes(&bytes));
println!("\nORIGINAL:");
println!(
" version: {} (0b{:04b})",
original.version, original.version
);
println!(" status: {} (Running)", original.status);
println!(" priority: {} (High)", original.priority);
println!("\nDECODED:");
println!(
" version: {} (0b{:04b})",
decoded.version, decoded.version
);
println!(" status: {} (Running)", decoded.status);
println!(" priority: {} (High)", decoded.priority);
assert_eq!(original.version, decoded.version, "Version mismatch");
assert_eq!(original.status, decoded.status, "Status mismatch");
assert_eq!(original.priority, decoded.priority, "Priority mismatch");
}
fn demo_flag_enums() {
print_section("6. FLAG ENUMS (Bitwise Operations)");
println!("\nFlag values:");
println!(
"FilePermissions::None = {} (0b{:08b})",
FilePermissions::None as u8,
FilePermissions::None as u8
);
println!(
"FilePermissions::Read = {} (0b{:08b})",
FilePermissions::Read as u8,
FilePermissions::Read as u8
);
println!(
"FilePermissions::Write = {} (0b{:08b})",
FilePermissions::Write as u8,
FilePermissions::Write as u8
);
println!(
"FilePermissions::Execute = {} (0b{:08b})",
FilePermissions::Execute as u8,
FilePermissions::Execute as u8
);
println!(
"FilePermissions::Delete = {} (0b{:08b})",
FilePermissions::Delete as u8,
FilePermissions::Delete as u8
);
println!("\nFlag combinations:");
let read_write = FilePermissions::Read | FilePermissions::Write;
println!("Read | Write = {read_write} (0b{read_write:08b})");
let all_perms = FilePermissions::Read
| FilePermissions::Write
| FilePermissions::Execute
| FilePermissions::Delete;
println!("All perms = {all_perms} (0b{all_perms:08b})");
let original = SecurityContext {
user_id: 15,
group_id: 3,
permissions: FilePermissions::Read | FilePermissions::Execute,
network_flags: NetworkFlags::Connected | NetworkFlags::Authenticated,
};
let bytes = original.to_be_bytes();
let (decoded, _) = SecurityContext::try_from_be_bytes(&bytes).unwrap();
println!("\nSecurityContext Comparison:");
println!("Serialized: {}", print_bytes(&bytes));
println!("\nORIGINAL:");
println!(" user_id: {}", original.user_id);
println!(" group_id: {}", original.group_id);
println!(
" permissions: {} (0b{:08b})",
original.permissions, original.permissions
);
println!(
" network_flags: {} (0b{:08b})",
original.network_flags, original.network_flags
);
println!("\nDECODED:");
println!(" user_id: {}", decoded.user_id);
println!(" group_id: {}", decoded.group_id);
println!(
" permissions: {} (0b{:08b})",
decoded.permissions, decoded.permissions
);
println!(
" network_flags: {} (0b{:08b})",
decoded.network_flags, decoded.network_flags
);
assert_eq!(original.user_id, decoded.user_id, "User ID mismatch");
assert_eq!(original.group_id, decoded.group_id, "Group ID mismatch");
assert_eq!(
original.permissions, decoded.permissions,
"Permissions mismatch"
);
assert_eq!(
original.network_flags, decoded.network_flags,
"Network flags mismatch"
);
}
fn demo_options() {
print_section("7. OPTION TYPE DEMONSTRATION");
let some_val = Optional {
optional_number: Some(42),
};
let bytes = some_val.to_be_bytes();
let (decoded, _) = Optional::try_from_be_bytes(&bytes).unwrap();
compare_values("Optional (Some)", &some_val, &decoded, &bytes);
let none_val = Optional {
optional_number: None,
};
let bytes = none_val.to_be_bytes();
let (decoded, _) = Optional::try_from_be_bytes(&bytes).unwrap();
compare_values("Optional (None)", &none_val, &decoded, &bytes);
}
fn demo_vectors() {
print_section("8. VECTOR DEMONSTRATION");
let with_vec = WithTailingVec {
pre_tail: 3,
tail: vec![10, 20, 30],
post_tail: 99,
};
let bytes = with_vec.to_be_bytes();
let (decoded, _) = WithTailingVec::try_from_be_bytes(&bytes).unwrap();
compare_values("WithTailingVec", &with_vec, &decoded, &bytes);
let with_size = WithSizeStruct {
innocent: 1,
real_tail: vec![2, 3, 4],
};
let bytes = with_size.to_be_bytes();
let (decoded, _) = WithSizeStruct::try_from_be_bytes(&bytes).unwrap();
compare_values("WithSizeStruct", &with_size, &decoded, &bytes);
}
fn demo_nested_structs() {
print_section("9. NESTED STRUCT DEMONSTRATION");
let dummy = DummyStruct {
dummy0: [0, 2],
dummy1: 1,
dummy2: 2,
};
let error_estimate = ErrorEstimate {
s_bit: 1,
z_bit: 0,
scale: 63,
dummy_struct: dummy.clone(),
};
let nested = NestedStruct {
dummy_struct: dummy,
optional_number: Some(42),
error_estimate,
};
let bytes = nested.to_be_bytes();
let (decoded, _) = NestedStruct::try_from_be_bytes(&bytes).unwrap();
compare_values("NestedStruct", &nested, &decoded, &bytes);
}
fn demo_complete_functionality() {
print_section("10. COMPLETE FUNCTIONALITY EXAMPLE");
let complete = CompleteFunctionality {
first: 1,
second: 2,
third: 3,
with_size: vec![6, 7, 8],
fourth: 4,
body: vec![5, 4, 3, 2],
u_16: U16 {
first: 1,
second: 2,
fourth: 1,
},
arrayed: ArrayedStruct {
mode: Modes { bits: 1 },
key_id: [2],
token: [3, 3],
client_iv: [4, 4, 4],
},
dummy_enum: DummyEnum::ServerStart,
optional: Some(5),
modes: Modes { bits: 3 },
vecty: WithTailingVec {
pre_tail: 4,
tail: vec![1, 2, 3, 4],
post_tail: 5,
},
u_32: U32 {
first: 1,
second: 57,
fourth: 1,
},
rattle: vec![1, 2, 3, 4, 5],
};
let bytes = complete.to_be_bytes();
println!("\nComplete struct size: {} bytes", bytes.len());
println!("First 20 bytes: {:?}", &bytes[..20.min(bytes.len())]);
let (decoded, _) = CompleteFunctionality::try_from_be_bytes(&bytes).unwrap();
println!("\nField comparison:");
println!("first: {} | {}", complete.first, decoded.first);
println!("second: {} | {}", complete.second, decoded.second);
println!("third: {} | {}", complete.third, decoded.third);
println!(
"with_size: {:?} | {:?}",
complete.with_size, decoded.with_size
);
println!("fourth: {} | {}", complete.fourth, decoded.fourth);
println!("body: {:?} | {:?}", complete.body, decoded.body);
println!(
"dummy_enum: {:?} | {:?}",
complete.dummy_enum, decoded.dummy_enum
);
println!(
"optional: {:?} | {:?}",
complete.optional, decoded.optional
);
println!("rattle: {:?} | {:?}", complete.rattle, decoded.rattle);
assert_eq!(complete, decoded, "Complete functionality test failed");
}
#[derive(BeBytes, Debug, PartialEq)]
struct U8 {
#[bits(1)]
first: u8,
#[bits(3)]
second: u8,
#[bits(4)]
third: u8,
fourth: u8,
}
#[derive(BeBytes, Debug, PartialEq, Copy, Clone)]
struct U16 {
#[bits(1)]
first: u8,
#[bits(14)]
second: u16,
#[bits(1)]
fourth: u8,
}
#[derive(BeBytes, Debug, PartialEq, Clone)]
struct U32 {
#[bits(1)]
first: u8,
#[bits(30)]
second: u32,
#[bits(1)]
fourth: u8,
}
#[derive(BeBytes, Debug, PartialEq)]
struct _U64 {
#[bits(1)]
first: u8,
#[bits(62)]
second: u64,
#[bits(1)]
fourth: u8,
}
#[derive(BeBytes, Debug, PartialEq)]
struct _U128 {
#[bits(1)]
first: u8,
#[bits(126)]
second: u128,
#[bits(1)]
fourth: u8,
}
#[derive(BeBytes, Debug, PartialEq)]
struct _I8 {
#[bits(1)]
first: u8,
#[bits(6)]
second: i8,
#[bits(1)]
fourth: u8,
}
#[derive(BeBytes, Debug, PartialEq, Copy, Clone)]
pub enum DummyEnum {
SetupResponse,
ServerStart,
SetupRequest,
}
#[derive(BeBytes, Debug, PartialEq, Clone)]
pub struct DummyStruct {
pub dummy0: [u8; 2],
#[bits(1)]
pub dummy1: u8,
#[bits(7)]
pub dummy2: u8,
}
#[derive(BeBytes, Debug, PartialEq, Clone)]
pub struct ErrorEstimate {
#[bits(1)]
pub s_bit: u8,
#[bits(1)]
pub z_bit: u8,
#[bits(6)]
pub scale: u8,
pub dummy_struct: DummyStruct,
}
#[derive(BeBytes, Debug, PartialEq)]
pub struct ErrorEstimateMini {
#[bits(1)]
pub s_bit: u8,
#[bits(1)]
pub z_bit: u8,
#[bits(6)]
pub scale: u8,
pub multiplier: u32,
}
#[derive(BeBytes, Debug, PartialEq)]
pub struct NestedStruct {
pub dummy_struct: DummyStruct,
pub optional_number: Option<i32>,
pub error_estimate: ErrorEstimate,
}
#[derive(BeBytes, Debug, PartialEq)]
pub struct Optional {
pub optional_number: Option<i32>,
}
#[derive(BeBytes, Debug, PartialEq, Clone)]
pub struct SmallStruct {
pub small: u8,
}
#[derive(BeBytes, Debug, PartialEq)]
pub struct SmallStructFather {
small_struct: SmallStruct,
num1: u32,
}
#[derive(BeBytes, Debug, PartialEq, Clone)]
pub struct ArrayedStruct {
pub mode: Modes,
pub key_id: [u8; 1],
pub token: [u8; 2],
pub client_iv: [u8; 3],
}
#[derive(BeBytes, Debug, PartialEq, Clone, Default)]
pub struct Modes {
pub bits: u8,
}
#[derive(Debug, PartialEq, Clone, BeBytes)]
struct WithTailingVec {
pre_tail: u8,
#[FromField(pre_tail)]
tail: Vec<u8>,
post_tail: u8,
}
#[derive(Debug, PartialEq, Clone, BeBytes)]
struct _InnocentStruct {
innocent: u8,
real_tail: Vec<u8>,
}
#[derive(Debug, PartialEq, Clone, BeBytes)]
struct WithSizeStruct {
innocent: u8,
#[With(size(3))]
real_tail: Vec<u8>,
}
#[derive(BeBytes, Debug, Clone, PartialEq)]
struct _DnsNameSegment {
length: u8,
#[FromField(length)]
segment: Vec<u8>,
}
#[derive(BeBytes, Debug, PartialEq)]
struct _DnsName {
segments: Vec<_DnsNameSegment>,
}
#[derive(BeBytes, Debug, PartialEq)]
struct CompleteFunctionality {
#[bits(1)]
first: u8,
#[bits(3)]
second: u8,
#[bits(4)]
third: u8,
#[With(size(3))]
with_size: Vec<u8>,
fourth: u8,
#[FromField(fourth)]
body: Vec<u8>,
u_16: U16,
arrayed: ArrayedStruct,
dummy_enum: DummyEnum,
optional: Option<i32>,
modes: Modes,
vecty: WithTailingVec,
u_32: U32,
rattle: Vec<u8>,
}
#[derive(BeBytes, Debug, PartialEq, Copy, Clone)]
enum _Status {
Idle = 0,
Running = 1,
Paused = 2,
Stopped = 3,
}
#[derive(BeBytes, Debug, PartialEq, Copy, Clone)]
enum _Priority {
Low = 0,
Medium = 1,
High = 2,
}
#[derive(BeBytes, Debug, PartialEq)]
struct PacketHeader {
#[bits(4)]
version: u8,
#[bits(2)] status: u8,
#[bits(2)] priority: u8,
}
#[derive(BeBytes, Debug, PartialEq, Copy, Clone)]
enum _LargeEnum {
V0 = 0,
V1 = 1,
V2 = 2,
V3 = 3,
V4 = 4,
V5 = 5,
V6 = 6,
V7 = 7,
V8 = 8,
V9 = 9,
V10 = 10,
V11 = 11,
V12 = 12,
V13 = 13,
V14 = 14,
V15 = 15,
V16 = 16,
}
#[derive(BeBytes, Debug, PartialEq)]
struct _ComplexPacket {
#[bits(3)]
flags: u8,
#[bits(5)] large_enum: u8,
payload_size: u16,
#[FromField(payload_size)]
payload: Vec<u8>,
}
#[derive(BeBytes, Debug, PartialEq, Copy, Clone)]
#[bebytes(flags)]
enum FilePermissions {
None = 0,
Read = 1,
Write = 2,
Execute = 4,
Delete = 8,
}
#[derive(BeBytes, Debug, PartialEq, Copy, Clone)]
#[bebytes(flags)]
enum NetworkFlags {
Connected = 1,
Authenticated = 2,
Encrypted = 4,
Compressed = 8,
KeepAlive = 16,
}
#[derive(BeBytes, Debug, PartialEq)]
struct SecurityContext {
#[bits(5)]
user_id: u8,
#[bits(3)]
group_id: u8,
permissions: u8, network_flags: u8, }
#[derive(BeBytes, Debug, PartialEq, Clone)]
struct SimpleHeader {
version: u8,
payload_length: u16,
flags: u8,
}
#[derive(BeBytes, Debug, PartialEq)]
struct SimplePacket {
header: SimpleHeader,
#[FromField(header.payload_length)]
payload: Vec<u8>,
checksum: u16,
}
#[derive(BeBytes, Debug, PartialEq, Clone)]
struct MetaInfo {
protocol_version: u8,
total_segments: u32, }
#[derive(BeBytes, Debug, PartialEq, Clone)]
struct ExtendedHeader {
magic_number: u32,
meta: MetaInfo,
}
#[derive(BeBytes, Debug, PartialEq, Clone)]
struct Container {
header: ExtendedHeader,
sequence_number: u16,
}
#[derive(BeBytes, Debug, PartialEq)]
struct ComplexProtocolPacket {
container: Container,
#[FromField(container.header.meta.total_segments)]
segments: Vec<Segment>,
trailer: u32,
}
#[derive(BeBytes, Debug, PartialEq, Clone)]
struct Segment {
#[bits(16)]
id: u16,
#[bits(16)]
length: u16,
data: [u8; 4],
}
#[allow(clippy::too_many_lines)]
fn demo_nested_field_access() {
print_section("Nested Field Access");
println!("\n1-Level Nesting: #[FromField(header.payload_length)]");
let simple_packet = SimplePacket {
header: SimpleHeader {
version: 1,
payload_length: 5,
flags: 0x42,
},
payload: vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE],
checksum: 0x1234,
};
let bytes = simple_packet.to_be_bytes();
println!("Serialized: {:02X?} ({} bytes)", bytes, bytes.len());
let (decoded, _) = SimplePacket::try_from_be_bytes(&bytes).unwrap();
println!("\nORIGINAL | DECODED");
println!("---------------------------------+---------------------------------");
println!("SimplePacket {{ | SimplePacket {{");
println!(" header: {{ | header: {{");
println!(
" version: {}, | version: {},",
simple_packet.header.version, decoded.header.version
);
println!(
" payload_length: {}, | payload_length: {},",
simple_packet.header.payload_length, decoded.header.payload_length
);
println!(
" flags: 0x{:02X}, | flags: 0x{:02X},",
simple_packet.header.flags, decoded.header.flags
);
println!(" }}, | }},");
println!(
" payload: {:02X?}, | payload: {:02X?},",
simple_packet.payload, decoded.payload
);
println!(
" checksum: 0x{:04X}, | checksum: 0x{:04X},",
simple_packet.checksum, decoded.checksum
);
println!("}} | }}");
assert_eq!(simple_packet, decoded, "Simple packet test failed");
println!("\n3-Level Nesting: #[FromField(container.header.meta.total_segments)]");
let complex_packet = ComplexProtocolPacket {
container: Container {
header: ExtendedHeader {
magic_number: 0xDEAD_BEEF,
meta: MetaInfo {
protocol_version: 2,
total_segments: 3,
},
},
sequence_number: 42,
},
segments: vec![
Segment {
id: 1,
length: 100,
data: [0x11, 0x22, 0x33, 0x44],
},
Segment {
id: 2,
length: 200,
data: [0x55, 0x66, 0x77, 0x88],
},
Segment {
id: 3,
length: 300,
data: [0x99, 0xAA, 0xBB, 0xCC],
},
],
trailer: 0xCAFE_BABE,
};
let bytes = complex_packet.to_be_bytes();
println!(
"Serialized: {} bytes (showing first 20: {:02X?}...)",
bytes.len(),
&bytes[..20.min(bytes.len())]
);
let (decoded, _) = ComplexProtocolPacket::try_from_be_bytes(&bytes).unwrap();
println!("\nField paths:");
println!(
"container.header.meta.total_segments = {} (controls segments vec size)",
decoded.container.header.meta.total_segments
);
println!("\nORIGINAL | DECODED");
println!("---------------------------------+---------------------------------");
println!("Container {{ | Container {{");
println!(
" header.magic_number: 0x{:08X} | header.magic_number: 0x{:08X}",
complex_packet.container.header.magic_number, decoded.container.header.magic_number
);
println!(
" header.meta.protocol_ver: {} | header.meta.protocol_ver: {}",
complex_packet.container.header.meta.protocol_version,
decoded.container.header.meta.protocol_version
);
println!(
" header.meta.total_segments: {} | header.meta.total_segments: {}",
complex_packet.container.header.meta.total_segments,
decoded.container.header.meta.total_segments
);
println!(
" sequence_number: {} | sequence_number: {}",
complex_packet.container.sequence_number, decoded.container.sequence_number
);
println!("}} | }}");
println!(
"segments: [{} items] | segments: [{} items]",
complex_packet.segments.len(),
decoded.segments.len()
);
for i in 0..complex_packet.segments.len() {
println!(
" [{}] id={}, size={} | [{}] id={}, size={}",
i,
complex_packet.segments[i].id,
complex_packet.segments[i].length,
i,
decoded.segments[i].id,
decoded.segments[i].length
);
}
println!(
"trailer: 0x{:08X} | trailer: 0x{:08X}",
complex_packet.trailer, decoded.trailer
);
assert_eq!(complex_packet, decoded, "Complex packet test failed");
}
fn demo_char_support() {
print_section("11. CHARACTER SUPPORT DEMONSTRATION");
let char_struct = CharStruct {
letter: 'A',
symbol: '€',
emoji: '🦀',
};
let bytes = char_struct.to_be_bytes();
let (decoded, _) = CharStruct::try_from_be_bytes(&bytes).unwrap();
compare_values("CharStruct", &char_struct, &decoded, &bytes);
println!("\nCharacter values:");
println!(
"letter: '{}' (U+{:04X})",
char_struct.letter, char_struct.letter as u32
);
println!(
"symbol: '{}' (U+{:04X})",
char_struct.symbol, char_struct.symbol as u32
);
println!(
"emoji: '{}' (U+{:04X})",
char_struct.emoji, char_struct.emoji as u32
);
let bit_char = CharBitField {
flags: 0b101,
compressed_char: 'Z',
reserved: 0b11,
};
let bytes = bit_char.to_be_bytes();
let (decoded, _) = CharBitField::try_from_be_bytes(&bytes).unwrap();
compare_values("CharBitField", &bit_char, &decoded, &bytes);
println!("\nBit field char demonstration:");
println!("flags: {:03b}", bit_char.flags);
println!("compressed_char: '{}' (16 bits)", bit_char.compressed_char);
println!("reserved: {:02b}", bit_char.reserved);
}
fn demo_string_support() {
print_section("12. STRING SUPPORT DEMONSTRATION");
println!("\nFixed-size string (16 bytes):");
let fixed_string = FixedStringStruct {
username: "alice".to_string() + &" ".repeat(11), status_code: 200,
};
let bytes = fixed_string.to_be_bytes();
let (decoded, _) = FixedStringStruct::try_from_be_bytes(&bytes).unwrap();
println!(
"Original username: '{}' ({} bytes)",
fixed_string.username,
fixed_string.username.len()
);
println!(
"Decoded username: '{}' ({} bytes)",
decoded.username,
decoded.username.len()
);
assert_eq!(fixed_string.status_code, decoded.status_code);
println!("\nVariable-size string with length field:");
let var_string = VariableStringStruct {
name_len: 7,
name: "BeBytes".to_string(),
desc_len: 35,
description: "A Rust serialization library".to_string() + " rocks!",
};
let bytes = var_string.to_be_bytes();
let (decoded, _) = VariableStringStruct::try_from_be_bytes(&bytes).unwrap();
compare_values("VariableStringStruct", &var_string, &decoded, &bytes);
println!("\nUnbounded string as last field:");
let log_entry = LogEntry {
timestamp: 1_640_995_200,
level: 3,
message: "Application started successfully with all modules loaded".to_string(),
};
let bytes = log_entry.to_be_bytes();
let (decoded, _) = LogEntry::try_from_be_bytes(&bytes).unwrap();
compare_values("LogEntry", &log_entry, &decoded, &bytes);
println!("\nMixed string types in one struct:");
let mixed = MixedStringStruct {
id: 42,
fixed_header: "BEBYTES_".to_string() + &" ".repeat(8), content_len: 13,
content: "Hello, world!".to_string(),
unicode_test: "Rust 🦀 rocks! 🚀".to_string(),
};
let bytes = mixed.to_be_bytes();
let (decoded, _) = MixedStringStruct::try_from_be_bytes(&bytes).unwrap();
println!("\nString comparison:");
println!(
"fixed_header: '{}' | '{}'",
mixed.fixed_header, decoded.fixed_header
);
println!("content: '{}' | '{}'", mixed.content, decoded.content);
println!(
"unicode_test: '{}' | '{}'",
mixed.unicode_test, decoded.unicode_test
);
assert_eq!(mixed.id, decoded.id);
assert_eq!(mixed.fixed_header, decoded.fixed_header);
assert_eq!(mixed.content, decoded.content);
assert_eq!(mixed.unicode_test, decoded.unicode_test);
}
#[derive(BeBytes, Debug, PartialEq)]
struct CharStruct {
letter: char,
symbol: char,
emoji: char,
}
#[derive(BeBytes, Debug, PartialEq)]
struct CharBitField {
#[bits(3)]
flags: u8,
#[bits(16)]
compressed_char: char,
#[bits(5)]
reserved: u8,
}
#[derive(BeBytes, Debug, PartialEq)]
struct FixedStringStruct {
#[With(size(16))]
username: String,
status_code: u16,
}
#[derive(BeBytes, Debug, PartialEq)]
struct VariableStringStruct {
name_len: u8,
#[FromField(name_len)]
name: String,
desc_len: u16,
#[FromField(desc_len)]
description: String,
}
#[derive(BeBytes, Debug, PartialEq)]
struct LogEntry {
timestamp: u64,
level: u8,
message: String, }
#[derive(BeBytes, Debug, PartialEq)]
struct MixedStringStruct {
id: u32,
#[With(size(16))]
fixed_header: String,
content_len: u8,
#[FromField(content_len)]
content: String,
unicode_test: String, }
fn demo_size_expressions() {
use bebytes_derive::BeBytes;
#[derive(BeBytes, Debug, PartialEq)]
struct MathExpression {
count: u8,
#[With(size(count * 4))]
data: Vec<u8>,
}
#[derive(BeBytes, Debug, PartialEq)]
struct FieldReference {
length: u16,
#[With(size(length))]
payload: Vec<u8>,
}
#[derive(BeBytes, Debug, PartialEq)]
struct ComplexExpression {
width: u8,
height: u8,
#[With(size((width * height) + 4))]
image_data: Vec<u8>,
}
#[derive(BeBytes, Debug, PartialEq)]
struct ProtocolMessage {
header_size: u8,
payload_count: u16,
#[With(size(header_size))]
header: Vec<u8>,
#[With(size(payload_count * 8))]
payload: Vec<u8>,
}
print_section("Size Expressions");
let math_msg = MathExpression {
count: 3,
data: vec![
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
], };
let bytes = math_msg.to_be_bytes();
let (decoded, _) = MathExpression::try_from_be_bytes(&bytes).unwrap();
compare_values(
"Mathematical Expression (count * 4)",
&math_msg,
&decoded,
&bytes,
);
let field_msg = FieldReference {
length: 8,
payload: vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22], };
let bytes = field_msg.to_be_bytes();
let (decoded, _) = FieldReference::try_from_be_bytes(&bytes).unwrap();
compare_values("Field Reference", &field_msg, &decoded, &bytes);
let complex_msg = ComplexExpression {
width: 4,
height: 3,
image_data: vec![0x10; 16], };
let bytes = complex_msg.to_be_bytes();
let (decoded, _) = ComplexExpression::try_from_be_bytes(&bytes).unwrap();
compare_values(
"Complex Expression ((width * height) + 4)",
&complex_msg,
&decoded,
&bytes,
);
let protocol_msg = ProtocolMessage {
header_size: 4,
payload_count: 2,
header: vec![0x01, 0x02, 0x03, 0x04], payload: vec![
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0x99, 0x00,
], };
let bytes = protocol_msg.to_be_bytes();
let (decoded, _) = ProtocolMessage::try_from_be_bytes(&bytes).unwrap();
compare_values(
"Protocol Message (multiple expressions)",
&protocol_msg,
&decoded,
&bytes,
);
println!("\nSize expressions enable dynamic field sizing based on other fields!");
println!("Supported operations: +, -, *, /, % with field references and literals");
}