1use std::io::Write;
2
3use crate::{
4 BinaryError, Node, NodeRef, Result,
5 decoder::Decoder,
6 encoder::{Encoder, build_marshaled_node_plan, build_marshaled_node_ref_plan},
7 node::{NodeContent, NodeContentRef},
8};
9
10const DEFAULT_MARSHAL_CAPACITY: usize = 1024;
11const AUTO_RESERVE_ATTRS_THRESHOLD: usize = 24;
12const AUTO_RESERVE_CHILDREN_THRESHOLD: usize = 64;
13const AUTO_RESERVE_SCALAR_THRESHOLD: usize = 8 * 1024;
14const AUTO_CHILD_SAMPLE_LIMIT: usize = 32;
15const AUTO_MAX_HINT_CAPACITY: usize = 512 * 1024;
16const AUTO_ATTR_ESTIMATE: usize = 24;
17const AUTO_CHILD_ESTIMATE: usize = 96;
18const AUTO_GRANDCHILD_ESTIMATE: usize = 40;
19
20pub fn unmarshal_ref(data: &[u8]) -> Result<NodeRef<'_>> {
21 let mut decoder = Decoder::new(data);
22 let node = decoder.read_node_ref()?;
23
24 if decoder.is_finished() {
25 Ok(node)
26 } else {
27 Err(BinaryError::LeftoverData(decoder.bytes_left()))
28 }
29}
30
31pub fn marshal_to(node: &Node, writer: &mut impl Write) -> Result<()> {
32 let mut encoder = Encoder::new(writer)?;
33 encoder.write_node(node)?;
34 Ok(())
35}
36
37pub fn marshal_to_vec(node: &Node, output: &mut Vec<u8>) -> Result<()> {
39 let mut encoder = Encoder::new_vec(output)?;
40 encoder.write_node(node)?;
41 Ok(())
42}
43
44pub fn marshal(node: &Node) -> Result<Vec<u8>> {
45 let mut payload = Vec::with_capacity(DEFAULT_MARSHAL_CAPACITY);
46 marshal_to_vec(node, &mut payload)?;
47 Ok(payload)
48}
49
50pub fn marshal_auto(node: &Node) -> Result<Vec<u8>> {
55 if should_auto_reserve_node(node) {
56 marshal_with_capacity(node, estimate_capacity_node(node))
57 } else {
58 marshal(node)
59 }
60}
61
62pub fn marshal_exact(node: &Node) -> Result<Vec<u8>> {
68 let plan = build_marshaled_node_plan(node);
69 let mut payload = vec![0; plan.size];
70 let mut encoder = Encoder::new_slice(payload.as_mut_slice(), Some(&plan.hints))?;
71 encoder.write_node(node)?;
72 let written = encoder.bytes_written();
73 if written != payload.len() || !plan.hints.fully_consumed() {
77 return Err(BinaryError::PlanMismatch);
78 }
79 Ok(payload)
80}
81
82pub fn marshal_ref_to(node: &NodeRef<'_>, writer: &mut impl Write) -> Result<()> {
85 let mut encoder = Encoder::new(writer)?;
86 encoder.write_node(node)?;
87 Ok(())
88}
89
90pub fn marshal_ref_to_vec(node: &NodeRef<'_>, output: &mut Vec<u8>) -> Result<()> {
92 let mut encoder = Encoder::new_vec(output)?;
93 encoder.write_node(node)?;
94 Ok(())
95}
96
97pub fn marshal_ref(node: &NodeRef<'_>) -> Result<Vec<u8>> {
100 let mut payload = Vec::with_capacity(DEFAULT_MARSHAL_CAPACITY);
101 marshal_ref_to_vec(node, &mut payload)?;
102 Ok(payload)
103}
104
105pub fn marshal_ref_auto(node: &NodeRef<'_>) -> Result<Vec<u8>> {
107 if should_auto_reserve_node_ref(node) {
108 marshal_ref_with_capacity(node, estimate_capacity_node_ref(node))
109 } else {
110 marshal_ref(node)
111 }
112}
113
114pub fn marshal_ref_exact(node: &NodeRef<'_>) -> Result<Vec<u8>> {
118 let plan = build_marshaled_node_ref_plan(node);
119 let mut payload = vec![0; plan.size];
120 let mut encoder = Encoder::new_slice(payload.as_mut_slice(), Some(&plan.hints))?;
121 encoder.write_node(node)?;
122 let written = encoder.bytes_written();
123 if written != payload.len() || !plan.hints.fully_consumed() {
125 return Err(BinaryError::PlanMismatch);
126 }
127 Ok(payload)
128}
129
130#[inline]
131fn marshal_with_capacity(node: &Node, capacity: usize) -> Result<Vec<u8>> {
132 let mut payload = Vec::with_capacity(capacity);
133 marshal_to_vec(node, &mut payload)?;
134 Ok(payload)
135}
136
137#[inline]
138fn marshal_ref_with_capacity(node: &NodeRef<'_>, capacity: usize) -> Result<Vec<u8>> {
139 let mut payload = Vec::with_capacity(capacity);
140 marshal_ref_to_vec(node, &mut payload)?;
141 Ok(payload)
142}
143
144#[inline]
145fn should_auto_reserve_node(node: &Node) -> bool {
146 if node.attrs.len() >= AUTO_RESERVE_ATTRS_THRESHOLD {
147 return true;
148 }
149
150 match &node.content {
151 Some(NodeContent::Bytes(bytes)) => bytes.len() >= AUTO_RESERVE_SCALAR_THRESHOLD,
152 Some(NodeContent::String(text)) => text.len() >= AUTO_RESERVE_SCALAR_THRESHOLD,
153 Some(NodeContent::Nodes(children)) => {
154 if children.len() >= AUTO_RESERVE_CHILDREN_THRESHOLD {
155 return true;
156 }
157 children.iter().any(|child| {
159 matches!(&child.content, Some(NodeContent::Nodes(gc)) if gc.len() >= AUTO_RESERVE_CHILDREN_THRESHOLD)
160 })
161 }
162 None => false,
163 }
164}
165
166#[inline]
167fn should_auto_reserve_node_ref(node: &NodeRef<'_>) -> bool {
168 if node.attrs.len() >= AUTO_RESERVE_ATTRS_THRESHOLD {
169 return true;
170 }
171
172 match node.content.as_ref() {
173 Some(NodeContentRef::Bytes(bytes)) => bytes.len() >= AUTO_RESERVE_SCALAR_THRESHOLD,
174 Some(NodeContentRef::String(text)) => text.len() >= AUTO_RESERVE_SCALAR_THRESHOLD,
175 Some(NodeContentRef::Nodes(children)) => {
176 if children.len() >= AUTO_RESERVE_CHILDREN_THRESHOLD {
177 return true;
178 }
179 children.iter().any(|child| {
181 matches!(child.content.as_ref(), Some(NodeContentRef::Nodes(gc)) if gc.len() >= AUTO_RESERVE_CHILDREN_THRESHOLD)
182 })
183 }
184 None => false,
185 }
186}
187
188#[inline]
189fn estimate_capacity_node(node: &Node) -> usize {
190 let mut estimate = DEFAULT_MARSHAL_CAPACITY + 16;
191 estimate += node.tag.len();
192 estimate += node.attrs.len() * AUTO_ATTR_ESTIMATE;
193
194 match &node.content {
195 Some(NodeContent::Bytes(bytes)) => {
196 estimate += bytes.len() + 8;
197 }
198 Some(NodeContent::String(text)) => {
199 estimate += text.len() + 8;
200 }
201 Some(NodeContent::Nodes(children)) => {
202 estimate += children.len() * AUTO_CHILD_ESTIMATE;
203 for child in children.iter().take(AUTO_CHILD_SAMPLE_LIMIT) {
204 estimate += child.tag.len() + child.attrs.len() * AUTO_ATTR_ESTIMATE;
205 match &child.content {
206 Some(NodeContent::Bytes(bytes)) => estimate += bytes.len() + 8,
207 Some(NodeContent::String(text)) => estimate += text.len() + 8,
208 Some(NodeContent::Nodes(grand_children)) => {
209 estimate += grand_children.len() * AUTO_GRANDCHILD_ESTIMATE;
210 }
211 None => {}
212 }
213 if estimate >= AUTO_MAX_HINT_CAPACITY {
214 return AUTO_MAX_HINT_CAPACITY;
215 }
216 }
217 }
218 None => {}
219 }
220
221 estimate.clamp(DEFAULT_MARSHAL_CAPACITY, AUTO_MAX_HINT_CAPACITY)
222}
223
224#[inline]
225fn estimate_capacity_node_ref(node: &NodeRef<'_>) -> usize {
226 let mut estimate = DEFAULT_MARSHAL_CAPACITY + 16;
227 estimate += node.tag.len();
228 estimate += node.attrs.len() * AUTO_ATTR_ESTIMATE;
229
230 match node.content.as_ref() {
231 Some(NodeContentRef::Bytes(bytes)) => {
232 estimate += bytes.len() + 8;
233 }
234 Some(NodeContentRef::String(text)) => {
235 estimate += text.len() + 8;
236 }
237 Some(NodeContentRef::Nodes(children)) => {
238 estimate += children.len() * AUTO_CHILD_ESTIMATE;
239 for child in children.iter().take(AUTO_CHILD_SAMPLE_LIMIT) {
240 estimate += child.tag.len() + child.attrs.len() * AUTO_ATTR_ESTIMATE;
241 match child.content.as_ref() {
242 Some(NodeContentRef::Bytes(bytes)) => estimate += bytes.len() + 8,
243 Some(NodeContentRef::String(text)) => estimate += text.len() + 8,
244 Some(NodeContentRef::Nodes(grand_children)) => {
245 estimate += grand_children.len() * AUTO_GRANDCHILD_ESTIMATE;
246 }
247 None => {}
248 }
249 if estimate >= AUTO_MAX_HINT_CAPACITY {
250 return AUTO_MAX_HINT_CAPACITY;
251 }
252 }
253 }
254 None => {}
255 }
256
257 estimate.clamp(DEFAULT_MARSHAL_CAPACITY, AUTO_MAX_HINT_CAPACITY)
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263 use crate::jid::Jid;
264 use crate::node::{Attrs, NodeContent, NodeValue};
265
266 type TestResult = Result<()>;
267
268 #[test]
282 fn interop_jid_carries_its_integrator_onto_the_wire() -> TestResult {
283 use crate::jid::Server;
284
285 fn node_for(jid: &Jid) -> Node {
286 let mut attrs = Attrs::with_capacity(1);
287 attrs.push("jid".to_string(), NodeValue::Jid(jid.clone()));
288 Node::new("iq", attrs, None)
289 }
290
291 fn encode_all(jid: &Jid) -> Vec<(&'static str, Vec<u8>)> {
292 let node = node_for(jid);
293 let r = node.as_node_ref();
294 vec![
295 ("marshal", marshal(&node).expect("marshal")),
296 ("marshal_exact", marshal_exact(&node).expect("exact")),
297 ("marshal_ref", marshal_ref(&r).expect("ref")),
298 (
299 "marshal_ref_exact",
300 marshal_ref_exact(&r).expect("ref exact"),
301 ),
302 ]
303 }
304
305 let base = Jid {
306 user: "123456789".into(),
307 server: Server::Interop,
308 agent: 0,
309 device: 7,
310 integrator: 300,
311 };
312
313 let other = Jid {
316 integrator: 301,
317 ..base.clone()
318 };
319 for ((path, a), (_, b)) in encode_all(&base).into_iter().zip(encode_all(&other)) {
320 assert_ne!(a, b, "{path}: the integrator must reach the wire");
321 assert!(
322 a.contains(&crate::token::INTEROP_JID),
323 "{path}: must use the INTEROP_JID token"
324 );
325 assert!(
327 a.windows(4).any(|w| w == [0x00, 0x07, 0x01, 0x2C]),
328 "{path}: device 7 and integrator 300 as u16 BE"
329 );
330 }
331
332 let encodings = encode_all(&base);
335 let (_, first) = &encodings[0];
336 for (path, bytes) in &encodings[1..] {
337 assert_eq!(bytes, first, "{path}: must agree with marshal");
338 }
339
340 let plain = Jid {
344 integrator: 0,
345 ..base.clone()
346 };
347 let bytes = marshal(&node_for(&plain))?;
348 assert!(
349 !bytes.contains(&crate::token::INTEROP_JID),
350 "no integrator, no INTEROP_JID token"
351 );
352 let decoded = unmarshal_ref(&bytes[1..])?;
353 let back = decoded
354 .attrs
355 .iter()
356 .find(|(k, _)| &**k == "jid")
357 .and_then(|(_, v)| v.to_jid())
358 .expect("jid attr");
359 assert_eq!(back.server, Server::Interop);
360 assert_eq!(back.device, 0, "JID_PAIR carries no device");
361
362 Ok(())
363 }
364
365 #[test]
370 fn ad_jid_round_trips_equal_through_the_wire_and_through_text() -> TestResult {
371 use crate::jid::Server;
372 use std::str::FromStr;
373
374 for server in [Server::Pn, Server::Lid, Server::Hosted, Server::HostedLid] {
375 let original = Jid {
376 user: "123456789012345".into(),
377 server,
378 agent: 0,
379 device: 7,
380 integrator: 0,
381 };
382
383 let mut attrs = Attrs::with_capacity(1);
384 attrs.push("jid".to_string(), NodeValue::Jid(original.clone()));
385 let node = Node::new("iq", attrs, None);
386
387 let bytes = marshal(&node)?;
389 let decoded = unmarshal_ref(&bytes[1..])?;
390 let from_wire = decoded
391 .attrs
392 .iter()
393 .find(|(k, _)| &**k == "jid")
394 .and_then(|(_, v)| v.to_jid())
395 .expect("jid attr survives the round-trip");
396
397 assert_eq!(
398 from_wire, original,
399 "{server:?}: encode -> decode must be idempotent"
400 );
401
402 let from_text = Jid::from_str(&from_wire.to_string()).expect("renders parseably");
403 assert_eq!(
404 from_wire, from_text,
405 "{server:?}: a wire-decoded JID must equal the same JID read back as text"
406 );
407 }
408
409 Ok(())
410 }
411
412 fn fixture_node() -> Node {
413 let mut attrs = Attrs::with_capacity(4);
414 attrs.push("id".to_string(), "ABC123");
415 attrs.push("to".to_string(), "123456789@s.whatsapp.net");
416 attrs.push(
417 "participant".to_string(),
418 NodeValue::Jid("15551234567@s.whatsapp.net".parse::<Jid>().unwrap()),
419 );
420 attrs.push("hex".to_string(), "DEADBEEF");
421
422 let child = Node::new(
423 "item",
424 Attrs::new(),
425 Some(NodeContent::Bytes(vec![1, 2, 3, 4, 5, 6, 7, 8])),
426 );
427
428 Node::new(
429 "message",
430 attrs,
431 Some(NodeContent::Nodes(vec![
432 child,
433 Node::new(
434 "text",
435 Attrs::new(),
436 Some(NodeContent::String("hello".repeat(40).into())),
437 ),
438 ])),
439 )
440 }
441
442 fn large_binary_fixture() -> Node {
443 Node::new(
444 "message",
445 Attrs::new(),
446 Some(NodeContent::Bytes(vec![
447 0xAB;
448 AUTO_RESERVE_SCALAR_THRESHOLD + 2048
449 ])),
450 )
451 }
452
453 #[test]
454 fn test_marshaled_node_size_matches_output() -> TestResult {
455 let node = fixture_node();
456 let plan = build_marshaled_node_plan(&node);
457 let payload = marshal(&node)?;
458 assert_eq!(payload.len(), plan.size);
459 Ok(())
460 }
461
462 #[test]
469 fn test_exact_matches_plain_for_all_string_shapes() -> TestResult {
470 let mut attrs = Attrs::with_capacity(8);
471 attrs.push("to".to_string(), "15551234567@s.whatsapp.net");
472 attrs.push("from".to_string(), "15550000001:12@s.whatsapp.net");
473 attrs.push("participant".to_string(), "15550000002_1@lid");
474 attrs.push("broadcast".to_string(), "status@broadcast");
475 attrs.push("type".to_string(), "text");
476 attrs.push("count".to_string(), "12345");
477 attrs.push("hexish".to_string(), "0123ABCDEF");
478 attrs.push("plain".to_string(), "not_a_token_value");
479 attrs.push("empty_user".to_string(), "@s.whatsapp.net");
481 attrs.push(
483 "typed_jid".to_string(),
484 NodeValue::Jid("15550000003:7@s.whatsapp.net".parse::<Jid>().unwrap()),
485 );
486 let node = Node::new(
487 "iq",
488 attrs,
489 Some(NodeContent::Nodes(vec![
490 Node::new(
491 "text",
492 Attrs::new(),
493 Some(NodeContent::String("x".repeat(300).into())),
494 ),
495 Node::new("empty", Attrs::new(), Some(NodeContent::String("".into()))),
496 Node::new(
497 "bin",
498 Attrs::new(),
499 Some(NodeContent::Bytes(vec![0xAB; 64])),
500 ),
501 Node::new("leaf", Attrs::new(), None),
502 ])),
503 );
504
505 assert_eq!(marshal(&node)?, marshal_exact(&node)?);
506 let node_ref = node.as_node_ref();
507 assert_eq!(marshal_ref(&node_ref)?, marshal_ref_exact(&node_ref)?);
508 Ok(())
509 }
510
511 #[test]
512 fn test_marshaled_node_ref_size_matches_output() -> TestResult {
513 let node = fixture_node();
514 let node_ref = node.as_node_ref();
515 let plan = build_marshaled_node_ref_plan(&node_ref);
516 let payload = marshal_ref(&node_ref)?;
517 assert_eq!(payload.len(), plan.size);
518 Ok(())
519 }
520
521 #[test]
522 fn test_marshal_matches_marshal_to_bytes() -> TestResult {
523 let node = fixture_node();
524
525 let payload_alloc = marshal(&node)?;
526
527 let mut payload_writer = Vec::new();
528 marshal_to(&node, &mut payload_writer)?;
529
530 assert_eq!(payload_alloc, payload_writer);
531 Ok(())
532 }
533
534 #[test]
535 fn test_marshal_ref_matches_marshal_ref_to_bytes() -> TestResult {
536 let node = fixture_node();
537 let node_ref = node.as_node_ref();
538
539 let payload_alloc = marshal_ref(&node_ref)?;
540
541 let mut payload_writer = Vec::new();
542 marshal_ref_to(&node_ref, &mut payload_writer)?;
543
544 assert_eq!(payload_alloc, payload_writer);
545 Ok(())
546 }
547
548 #[test]
549 fn test_marshal_to_vec_matches_marshal_to() -> TestResult {
550 let node = fixture_node();
551
552 let mut payload_vec_writer = Vec::new();
553 marshal_to_vec(&node, &mut payload_vec_writer)?;
554
555 let mut payload_writer = Vec::new();
556 marshal_to(&node, &mut payload_writer)?;
557
558 assert_eq!(payload_vec_writer, payload_writer);
559 Ok(())
560 }
561
562 #[test]
563 fn test_marshal_ref_to_vec_matches_marshal_ref_to() -> TestResult {
564 let node = fixture_node();
565 let node_ref = node.as_node_ref();
566
567 let mut payload_vec_writer = Vec::new();
568 marshal_ref_to_vec(&node_ref, &mut payload_vec_writer)?;
569
570 let mut payload_writer = Vec::new();
571 marshal_ref_to(&node_ref, &mut payload_writer)?;
572
573 assert_eq!(payload_vec_writer, payload_writer);
574 Ok(())
575 }
576
577 #[test]
578 fn test_marshal_exact_matches_marshal_to_bytes() -> TestResult {
579 let node = fixture_node();
580
581 let payload_exact = marshal_exact(&node)?;
582
583 let mut payload_writer = Vec::new();
584 marshal_to(&node, &mut payload_writer)?;
585
586 assert_eq!(payload_exact, payload_writer);
587 Ok(())
588 }
589
590 #[test]
591 fn test_marshal_ref_exact_matches_marshal_ref_to_bytes() -> TestResult {
592 let node = fixture_node();
593 let node_ref = node.as_node_ref();
594
595 let payload_exact = marshal_ref_exact(&node_ref)?;
596
597 let mut payload_writer = Vec::new();
598 marshal_ref_to(&node_ref, &mut payload_writer)?;
599
600 assert_eq!(payload_exact, payload_writer);
601 Ok(())
602 }
603
604 #[test]
605 fn test_marshal_auto_matches_marshal_to_bytes() -> TestResult {
606 let node = fixture_node();
607 let payload_auto = marshal_auto(&node)?;
608
609 let mut payload_writer = Vec::new();
610 marshal_to(&node, &mut payload_writer)?;
611
612 assert_eq!(payload_auto, payload_writer);
613 Ok(())
614 }
615
616 #[test]
617 fn test_marshal_ref_auto_matches_marshal_ref_to_bytes() -> TestResult {
618 let node = fixture_node();
619 let node_ref = node.as_node_ref();
620 let payload_auto = marshal_ref_auto(&node_ref)?;
621
622 let mut payload_writer = Vec::new();
623 marshal_ref_to(&node_ref, &mut payload_writer)?;
624
625 assert_eq!(payload_auto, payload_writer);
626 Ok(())
627 }
628
629 #[test]
630 fn test_marshal_auto_large_binary_matches_marshal_to_bytes() -> TestResult {
631 let node = large_binary_fixture();
632 let payload_auto = marshal_auto(&node)?;
633
634 let mut payload_writer = Vec::new();
635 marshal_to(&node, &mut payload_writer)?;
636
637 assert_eq!(payload_auto, payload_writer);
638 Ok(())
639 }
640
641 #[test]
642 fn test_marshal_ref_auto_large_binary_matches_marshal_ref_to_bytes() -> TestResult {
643 let node = large_binary_fixture();
644 let node_ref = node.as_node_ref();
645 let payload_auto = marshal_ref_auto(&node_ref)?;
646
647 let mut payload_writer = Vec::new();
648 marshal_ref_to(&node_ref, &mut payload_writer)?;
649
650 assert_eq!(payload_auto, payload_writer);
651 Ok(())
652 }
653}