1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! Phase 2 ergonomics: `ctx` is **decode-only** (a `#[bin(ctx(...))]` type gets a plain
//! `BitEncode`/`to_bytes` unless its write side actually reads a ctx param), the generated
//! `…Ctx` struct has a positional `new`, and a variant `Vec` field can carry per-element
//! `ctx`.
mod macro_ {
use bnb::{BitError, Sink, Source, bin};
// ── A ctx type whose encode does NOT read ctx → plain encode (no `to_bytes_with`). ──
#[bin(big, ctx(len: u8))]
#[derive(Debug, PartialEq)]
struct Sized {
#[br(count = len)] // decode-only: encode iterates `data`
data: Vec<u8>,
}
#[test]
fn ctx_decode_only_means_plain_encode() {
let s = Sized {
data: vec![1, 2, 3],
};
// Encode is plain — no context needed on the write path.
assert_eq!(s.to_bytes().unwrap(), [1, 2, 3]);
// Decode still needs the context; build it positionally with `new`.
assert_eq!(
Sized::decode_with_exact(&[1, 2, 3], SizedCtx::new(3)).unwrap(),
s
);
}
// ── A ctx type whose encode DOES read ctx (a keyed transform) keeps `encode_with`. ──
#[bin(big, ctx(key: u8))]
#[derive(Debug, PartialEq)]
struct Masked {
#[br(map = |raw: u8| raw ^ key)] // decode: unmask with the session key
#[bw(map = |v: &u8| *v ^ key)] // encode: mask — the wire value depends on `key`
value: u8,
}
#[test]
fn encode_that_reads_ctx_keeps_encode_with() {
let m = Masked { value: 0x0F };
let bytes = m.to_bytes_with(MaskedCtx::new(0xFF)).unwrap();
assert_eq!(bytes, [0xF0]); // 0x0F ^ 0xFF
assert_eq!(
Masked::decode_with_exact(&bytes, MaskedCtx::new(0xFF)).unwrap(),
m
);
}
// A ctx-reading encode via a `write_with` *custom writer* (a capturing closure) — the body
// scan keeps `encode_with`, where a `calc`/`map`-only check would miss it and miscompile.
fn unmask<S: Source>(r: &mut S, key: u8) -> Result<u8, BitError> {
Ok(r.read::<u8>()? ^ key)
}
#[bin(big, ctx(key: u8))]
#[derive(Debug, PartialEq)]
struct Custom {
#[br(parse_with = |r| unmask(r, key))]
#[bw(write_with = |v: &u8, w: &mut _| Sink::write(w, *v ^ key))]
value: u8,
}
#[test]
fn encode_reading_ctx_via_write_with() {
let c = Custom { value: 0x0F };
assert_eq!(c.to_bytes_with(CustomCtx::new(0xFF)).unwrap(), [0xF0]);
assert_eq!(
Custom::decode_with_exact(&[0xF0], CustomCtx::new(0xFF)).unwrap(),
c
);
}
// ── Per-variant `ctx` on a `Vec` field: each element is a ctx-message taking a sibling. ──
#[bin(big, ctx(width: u8))]
#[derive(Debug, PartialEq)]
struct Cell {
#[br(count = width)]
bytes: Vec<u8>,
}
#[bin(big)]
#[derive(Debug, PartialEq)]
enum Grid {
#[bin(magic = 1u8)]
Rows {
width: u8,
#[br(temp)]
#[bw(calc = cells.len() as u8)]
count: u8,
#[br(count = count, ctx { width })] // hand `width` to every Cell
cells: Vec<Cell>,
},
}
#[test]
fn per_variant_ctx_on_a_vec_field() {
let g = Grid::Rows {
width: 2,
cells: vec![
Cell {
bytes: vec![0xAA, 0xBB],
},
Cell {
bytes: vec![0xCC, 0xDD],
},
],
};
let bytes = [0x01, 0x02, 0x02, 0xAA, 0xBB, 0xCC, 0xDD]; // magic, width, count, 2 cells
assert_eq!(g.to_bytes().unwrap(), bytes);
assert_eq!(Grid::decode_exact(&bytes).unwrap(), g);
}
}