1use super::codec::{Error, Error::*};
13
14pub const WIRE_U64: u8 = 1;
16pub const WIRE_BYTES: u8 = 2;
18
19pub struct FieldWriter {
21 buf: Vec<u8>,
22}
23
24impl Default for FieldWriter {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl FieldWriter {
31 pub fn new() -> Self {
32 Self {
33 buf: Vec::with_capacity(32),
34 }
35 }
36
37 pub fn bytes(&self) -> &[u8] {
39 &self.buf
40 }
41
42 pub fn into_vec(self) -> Vec<u8> {
43 self.buf
44 }
45
46 fn put_hdr(&mut self, tag: u16, kind: u8) {
47 self.buf.extend_from_slice(&tag.to_le_bytes());
48 self.buf.push(kind);
49 }
50
51 pub fn put_u64(&mut self, tag: u16, v: u64) {
54 self.put_hdr(tag, WIRE_U64);
55 self.buf.extend_from_slice(&v.to_le_bytes());
56 }
57
58 pub fn put_u16(&mut self, tag: u16, v: u16) {
59 self.put_u64(tag, u64::from(v));
60 }
61
62 pub fn put_u32(&mut self, tag: u16, v: u32) {
63 self.put_u64(tag, u64::from(v));
64 }
65
66 pub fn put_bool(&mut self, tag: u16, v: bool) {
67 self.put_u64(tag, u64::from(v));
68 }
69
70 pub fn put_bytes(&mut self, tag: u16, p: &[u8]) {
73 if p.is_empty() {
74 return;
75 }
76 self.put_hdr(tag, WIRE_BYTES);
77 self.buf.extend_from_slice(&(p.len() as u32).to_le_bytes());
78 self.buf.extend_from_slice(p);
79 }
80
81 pub fn put_string(&mut self, tag: u16, s: &str) {
82 if s.is_empty() {
83 return;
84 }
85 self.put_bytes(tag, s.as_bytes());
86 }
87
88 pub fn put_u16s(&mut self, tag: u16, vs: &[u16]) {
90 if vs.is_empty() {
91 return;
92 }
93 let mut inner = Vec::with_capacity(2 + vs.len() * 2);
94 inner.extend_from_slice(&(vs.len() as u16).to_le_bytes());
95 for v in vs {
96 inner.extend_from_slice(&v.to_le_bytes());
97 }
98 self.put_bytes(tag, &inner);
99 }
100}
101
102pub struct FieldReader<'a> {
104 b: &'a [u8],
105 i: usize,
106}
107
108impl<'a> FieldReader<'a> {
109 pub fn new(b: &'a [u8]) -> Self {
110 Self { b, i: 0 }
111 }
112
113 pub fn done(&self) -> bool {
114 self.i >= self.b.len()
115 }
116
117 fn need(&self, n: usize) -> Result<(), Error> {
118 if self.b.len() - self.i < n {
119 return Err(Truncated);
120 }
121 Ok(())
122 }
123
124 pub fn next_field(&mut self) -> Result<(u16, u8), Error> {
126 if self.done() {
127 return Err(Truncated);
128 }
129 self.need(3)?;
130 let tag = u16::from_le_bytes([self.b[self.i], self.b[self.i + 1]]);
131 let kind = self.b[self.i + 2];
132 self.i += 3;
133 if kind != WIRE_U64 && kind != WIRE_BYTES {
134 return Err(BadWire);
135 }
136 if tag == 0 {
137 return Err(BadTag);
138 }
139 Ok((tag, kind))
140 }
141
142 pub fn u64(&mut self) -> Result<u64, Error> {
144 self.need(8)?;
145 let v = u64::from_le_bytes(self.b[self.i..self.i + 8].try_into().unwrap());
146 self.i += 8;
147 Ok(v)
148 }
149
150 pub fn bytes(&mut self) -> Result<&'a [u8], Error> {
153 self.need(4)?;
154 let n = u32::from_le_bytes(self.b[self.i..self.i + 4].try_into().unwrap()) as usize;
155 self.i += 4;
156 self.need(n)?;
157 let out = &self.b[self.i..self.i + n];
158 self.i += n;
159 Ok(out)
160 }
161
162 pub fn skip(&mut self, kind: u8) -> Result<(), Error> {
164 match kind {
165 WIRE_U64 => {
166 self.u64()?;
167 Ok(())
168 }
169 WIRE_BYTES => {
170 self.bytes()?;
171 Ok(())
172 }
173 _ => Err(BadWire),
174 }
175 }
176}
177
178pub fn walk_fields(
181 b: &[u8],
182 mut f: impl FnMut(u16, u8, &mut FieldReader<'_>) -> Result<(), Error>,
183) -> Result<(), Error> {
184 let mut fr = FieldReader::new(b);
185 while !fr.done() {
186 let (tag, kind) = fr.next_field()?;
187 let before = fr.i;
188 f(tag, kind, &mut fr)?;
189 if fr.i == before {
191 fr.skip(kind)?;
192 }
193 }
194 Ok(())
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn writer_reader_roundtrip() {
203 let mut w = FieldWriter::new();
204 w.put_u64(1, 42);
205 w.put_u16(2, 7);
206 w.put_bool(3, true);
207 w.put_bool(4, false);
208 w.put_string(5, "hello");
209 w.put_bytes(6, &[1, 2, 3]);
210 w.put_u16s(7, &[5, 10]);
211
212 let mut seen = Vec::new();
213 walk_fields(w.bytes(), |tag, kind, fr| {
214 let v = match kind {
215 WIRE_U64 => fr.u64().unwrap(),
216 WIRE_BYTES => {
217 let b = fr.bytes().unwrap();
218 b.len() as u64
219 }
220 _ => unreachable!(),
221 };
222 seen.push((tag, v));
223 Ok(())
224 })
225 .unwrap();
226 assert_eq!(
227 seen,
228 vec![(1, 42), (2, 7), (3, 1), (4, 0), (5, 5), (6, 3), (7, 6)]
229 );
230 }
231
232 #[test]
233 fn empty_values_are_omitted() {
234 let mut w = FieldWriter::new();
235 w.put_string(1, "");
236 w.put_bytes(2, &[]);
237 w.put_u16s(3, &[]);
238 assert!(w.bytes().is_empty());
239 }
240
241 #[test]
242 fn unknown_tags_are_skippable() {
243 let mut w = FieldWriter::new();
244 w.put_u64(1, 9);
245 w.put_string(128, "experimental");
246 w.put_string(2, "known");
247
248 let mut name = String::new();
249 walk_fields(w.bytes(), |tag, kind, fr| {
250 match tag {
251 2 => name = String::from_utf8_lossy(fr.bytes().unwrap()).into_owned(),
252 _ => fr.skip(kind)?,
253 }
254 Ok(())
255 })
256 .unwrap();
257 assert_eq!(name, "known");
258 }
259
260 #[test]
261 fn rejects_bad_wire_kinds() {
262 let mut w = FieldWriter::new();
263 w.buf = vec![1, 0, 7]; let err = walk_fields(w.bytes(), |_t, _k, _fr| Ok(())).unwrap_err();
265 assert!(matches!(err, Error::BadWire));
266 }
267
268 #[test]
269 fn truncated_payload_errors() {
270 let mut w = FieldWriter::new();
271 w.put_u64(1, 42);
272 let bytes = w.into_vec();
273 let err = walk_fields(&bytes[..3], |_t, _k, _fr| Ok(())).unwrap_err();
274 assert!(matches!(err, Error::Truncated));
275 }
276}