1use crate::{varint, ProtoError, Result, PROTOCOL_VERSION};
2
3pub const MAX_SERVER_FILL_BYTES: usize = 32;
5
6#[derive(Debug, Clone, PartialEq, Eq, Default)]
43pub struct Params {
44 pub protocol_version: i64,
45 pub duration_ns: i64,
46 pub interval_ns: i64,
47 pub length: i64,
48 pub received_stats: ReceivedStats,
49 pub stamp_at: StampAt,
50 pub clock: Clock,
51 pub dscp: i64,
57 pub server_fill: Option<ServerFill>,
58}
59
60impl Params {
61 pub fn with_protocol_defaults() -> Self {
62 Self {
63 protocol_version: PROTOCOL_VERSION,
64 ..Self::default()
65 }
66 }
67
68 pub fn encode(&self) -> Vec<u8> {
73 let mut out = Vec::new();
74 push_int(1, self.protocol_version, &mut out);
75 push_int(2, self.duration_ns, &mut out);
76 push_int(3, self.interval_ns, &mut out);
77 push_int(4, self.length, &mut out);
78 push_int(5, self.received_stats as i64, &mut out);
79 push_int(6, self.stamp_at as i64, &mut out);
80 push_int(7, self.clock as i64, &mut out);
81 push_int(8, self.dscp, &mut out);
82 if let Some(fill) = &self.server_fill {
83 varint::encode_uvarint(9, &mut out);
84 varint::encode_uvarint(fill.value.len() as u64, &mut out);
85 out.extend_from_slice(fill.value.as_bytes());
86 }
87 out
88 }
89
90 pub fn decode(input: &[u8]) -> Result<Self> {
100 Self::decode_with_presence(input).map(|decoded| decoded.params)
101 }
102
103 pub fn decode_with_presence(input: &[u8]) -> Result<DecodedParams> {
130 let mut params = Self::default();
131 let mut presence = ParamPresence::default();
132 let mut pos = 0;
133 while pos < input.len() {
134 let (tag, used) = varint::decode_uvarint(&input[pos..])?;
135 pos += used;
136 match tag {
137 1 => {
138 params.protocol_version = read_int(input, &mut pos)?;
139 presence.protocol_version = true;
140 }
141 2 => {
142 params.duration_ns = read_int(input, &mut pos)?;
143 presence.duration_ns = true;
144 }
145 3 => {
146 params.interval_ns = read_int(input, &mut pos)?;
147 presence.interval_ns = true;
148 }
149 4 => {
150 params.length = read_int(input, &mut pos)?;
151 presence.length = true;
152 }
153 5 => {
154 params.received_stats = ReceivedStats::try_from(read_int(input, &mut pos)?)?;
155 presence.received_stats = true;
156 }
157 6 => {
158 params.stamp_at = StampAt::try_from(read_int(input, &mut pos)?)?;
159 presence.stamp_at = true;
160 }
161 7 => {
162 params.clock = Clock::try_from(read_int(input, &mut pos)?)?;
163 presence.clock = true;
164 }
165 8 => {
166 params.dscp = read_int(input, &mut pos)?;
167 presence.dscp = true;
168 }
169 9 => {
170 let (len, used) = varint::decode_uvarint(&input[pos..])?;
171 pos += used;
172 let len = usize::try_from(len)
173 .map_err(|_| ProtoError::ParameterLengthTooLarge { tag, length: len })?;
174 if len > MAX_SERVER_FILL_BYTES {
175 return Err(ProtoError::ParameterLengthTooLarge {
176 tag,
177 length: len as u64,
178 });
179 }
180 if input.len().saturating_sub(pos) < len {
181 return Err(ProtoError::MalformedParams);
182 }
183 let value = std::str::from_utf8(&input[pos..pos + len])
184 .map_err(|_| ProtoError::InvalidUtf8)?
185 .to_owned();
186 pos += len;
187 params.server_fill = Some(ServerFill { value });
188 presence.server_fill = true;
189 }
190 _ => {
191 let (_, used) = varint::decode_uvarint(&input[pos..])?;
192 pos += used;
193 }
194 }
195 }
196 Ok(DecodedParams { params, presence })
197 }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Default)]
204pub struct DecodedParams {
205 pub params: Params,
207 pub presence: ParamPresence,
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
216pub struct ParamPresence {
217 pub protocol_version: bool,
218 pub duration_ns: bool,
219 pub interval_ns: bool,
220 pub length: bool,
221 pub received_stats: bool,
222 pub stamp_at: bool,
223 pub clock: bool,
224 pub dscp: bool,
225 pub server_fill: bool,
226}
227
228#[derive(Debug, Clone, PartialEq, Eq)]
229pub struct ServerFill {
230 pub value: String,
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
234#[repr(i64)]
235pub enum ReceivedStats {
236 #[default]
237 None = 0,
238 Count = 1,
239 Window = 2,
240 Both = 3,
241}
242
243impl ReceivedStats {
244 pub fn has_count(self) -> bool {
245 matches!(self, Self::Count | Self::Both)
246 }
247
248 pub fn has_window(self) -> bool {
249 matches!(self, Self::Window | Self::Both)
250 }
251}
252
253impl TryFrom<i64> for ReceivedStats {
254 type Error = ProtoError;
255
256 fn try_from(value: i64) -> Result<Self> {
257 match value {
258 0 => Ok(Self::None),
259 1 => Ok(Self::Count),
260 2 => Ok(Self::Window),
261 3 => Ok(Self::Both),
262 _ => Err(ProtoError::InvalidEnum {
263 name: "ReceivedStats",
264 value,
265 }),
266 }
267 }
268}
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
271#[repr(i64)]
272pub enum StampAt {
273 #[default]
274 None = 0,
275 Send = 1,
276 Receive = 2,
277 Both = 3,
278 Midpoint = 4,
279}
280
281impl TryFrom<i64> for StampAt {
282 type Error = ProtoError;
283
284 fn try_from(value: i64) -> Result<Self> {
285 match value {
286 0 => Ok(Self::None),
287 1 => Ok(Self::Send),
288 2 => Ok(Self::Receive),
289 3 => Ok(Self::Both),
290 4 => Ok(Self::Midpoint),
291 _ => Err(ProtoError::InvalidEnum {
292 name: "StampAt",
293 value,
294 }),
295 }
296 }
297}
298
299#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
316#[repr(i64)]
317pub enum Clock {
318 #[default]
321 Unspecified = 0,
322 Wall = 1,
323 Monotonic = 2,
324 Both = 3,
325}
326
327impl Clock {
328 pub fn has_wall(self) -> bool {
329 matches!(self, Self::Wall | Self::Both)
330 }
331
332 pub fn has_mono(self) -> bool {
333 matches!(self, Self::Monotonic | Self::Both)
334 }
335}
336
337impl TryFrom<i64> for Clock {
338 type Error = ProtoError;
339
340 fn try_from(value: i64) -> Result<Self> {
346 match value {
347 1 => Ok(Self::Wall),
348 2 => Ok(Self::Monotonic),
349 3 => Ok(Self::Both),
350 _ => Err(ProtoError::InvalidEnum {
351 name: "Clock",
352 value,
353 }),
354 }
355 }
356}
357
358fn push_int(tag: u64, value: i64, out: &mut Vec<u8>) {
359 if value == 0 {
360 return;
361 }
362 varint::encode_uvarint(tag, out);
363 varint::encode_varint(value, out);
364}
365
366fn read_int(input: &[u8], pos: &mut usize) -> Result<i64> {
367 let (value, used) = varint::decode_varint(&input[*pos..])?;
368 *pos += used;
369 Ok(value)
370}
371
372#[cfg(test)]
373mod tests {
374 use super::*;
375
376 fn assert_round_trip(params: Params) {
377 assert_eq!(Params::decode(¶ms.encode()), Ok(params));
378 }
379
380 fn encode_server_fill_value(value: &[u8]) -> Vec<u8> {
381 let mut encoded = Vec::new();
382 varint::encode_uvarint(9, &mut encoded);
383 varint::encode_uvarint(value.len() as u64, &mut encoded);
384 encoded.extend_from_slice(value);
385 encoded
386 }
387
388 fn encode_int(tag: u64, value: i64) -> Vec<u8> {
389 let mut encoded = Vec::new();
390 varint::encode_uvarint(tag, &mut encoded);
391 varint::encode_varint(value, &mut encoded);
392 encoded
393 }
394
395 #[test]
396 fn params_round_trip() {
397 let params = Params {
398 protocol_version: 1,
399 duration_ns: 3_000_000_000,
400 interval_ns: 1_000_000_000,
401 length: 1472,
402 received_stats: ReceivedStats::Both,
403 stamp_at: StampAt::Both,
404 clock: Clock::Both,
405 dscp: 184,
406 server_fill: Some(ServerFill {
407 value: "pattern:abc".to_owned(),
408 }),
409 };
410 assert_round_trip(params);
411 }
412
413 #[test]
414 fn params_round_trip_negotiated_option_modes() {
415 for received_stats in [
416 ReceivedStats::None,
417 ReceivedStats::Count,
418 ReceivedStats::Window,
419 ReceivedStats::Both,
420 ] {
421 assert_round_trip(Params {
422 protocol_version: 1,
423 received_stats,
424 ..Params::default()
425 });
426 }
427
428 for stamp_at in [
429 StampAt::None,
430 StampAt::Send,
431 StampAt::Receive,
432 StampAt::Both,
433 StampAt::Midpoint,
434 ] {
435 assert_round_trip(Params {
436 protocol_version: 1,
437 stamp_at,
438 clock: Clock::Both,
439 ..Params::default()
440 });
441 }
442
443 for clock in [Clock::Wall, Clock::Monotonic, Clock::Both] {
444 assert_round_trip(Params {
445 protocol_version: 1,
446 stamp_at: StampAt::Both,
447 clock,
448 ..Params::default()
449 });
450 }
451 }
452
453 #[test]
454 fn params_round_trip_dscp_values_are_encoded_without_shifting() {
455 for dscp in [0, 46, 63, 64, 184, -1] {
460 let params = Params {
461 protocol_version: 1,
462 dscp,
463 ..Params::default()
464 };
465 assert_round_trip(params);
466 }
467
468 let params = Params {
469 protocol_version: 1,
470 dscp: 46,
471 ..Params::default()
472 };
473 let encoded = params.encode();
474 assert!(
475 encoded.windows(2).any(|bytes| bytes == [8, 92]),
476 "dscp value 46 must be encoded as param value 46"
477 );
478 assert!(
479 !encoded.windows(3).any(|bytes| bytes == [8, 0xf0, 0x02]),
480 "Params::encode must not shift dscp 46 to 184; any codepoint shift is a caller concern"
481 );
482 assert_eq!(Params::decode(&encoded).unwrap().dscp, 46);
483 }
484
485 #[test]
486 fn server_fill_absent_short_and_max_length_round_trip() {
487 assert_round_trip(Params {
488 protocol_version: 1,
489 server_fill: None,
490 ..Params::default()
491 });
492
493 assert_round_trip(Params {
494 protocol_version: 1,
495 server_fill: Some(ServerFill {
496 value: "rand".to_owned(),
497 }),
498 ..Params::default()
499 });
500
501 assert_round_trip(Params {
502 protocol_version: 1,
503 server_fill: Some(ServerFill {
504 value: "0123456789abcdef0123456789abcdef".to_owned(),
505 }),
506 ..Params::default()
507 });
508 }
509
510 #[test]
511 fn server_fill_decode_accepts_max_length() {
512 let value = b"0123456789abcdef0123456789abcdef";
513 let params = Params::decode(&encode_server_fill_value(value)).unwrap();
514
515 assert_eq!(
516 params.server_fill,
517 Some(ServerFill {
518 value: "0123456789abcdef0123456789abcdef".to_owned(),
519 })
520 );
521 }
522
523 #[test]
524 fn server_fill_decode_rejects_oversized_length() {
525 let value = b"0123456789abcdef0123456789abcdefx";
526
527 assert_eq!(
528 Params::decode(&encode_server_fill_value(value)),
529 Err(ProtoError::ParameterLengthTooLarge { tag: 9, length: 33 })
530 );
531 }
532
533 #[test]
534 fn server_fill_tag_and_length_are_encoded_before_utf8_bytes() {
535 let params = Params {
536 protocol_version: 1,
537 server_fill: Some(ServerFill {
538 value: "rand".to_owned(),
539 }),
540 ..Params::default()
541 };
542 let encoded = params.encode();
543 assert!(encoded
544 .windows(6)
545 .any(|bytes| bytes == [9, 4, b'r', b'a', b'n', b'd']));
546 }
547
548 #[test]
549 fn unknown_tags_are_ignored() {
550 let mut encoded = Vec::new();
551 varint::encode_uvarint(99, &mut encoded);
552 varint::encode_varint(123, &mut encoded);
553 varint::encode_uvarint(1, &mut encoded);
554 varint::encode_varint(1, &mut encoded);
555
556 let params = Params::decode(&encoded).unwrap();
557 assert_eq!(params.protocol_version, 1);
558 }
559
560 #[test]
561 fn invalid_received_stats_value_is_rejected() {
562 let mut encoded = Vec::new();
563 varint::encode_uvarint(5, &mut encoded);
564 varint::encode_varint(4, &mut encoded);
565
566 assert_eq!(
567 Params::decode(&encoded),
568 Err(ProtoError::InvalidEnum {
569 name: "ReceivedStats",
570 value: 4,
571 })
572 );
573 }
574
575 #[test]
576 fn invalid_timestamp_value_is_rejected() {
577 let mut encoded = Vec::new();
578 varint::encode_uvarint(6, &mut encoded);
579 varint::encode_varint(5, &mut encoded);
580
581 assert_eq!(
582 Params::decode(&encoded),
583 Err(ProtoError::InvalidEnum {
584 name: "StampAt",
585 value: 5,
586 })
587 );
588 }
589
590 #[test]
591 fn explicit_clock_zero_is_rejected() {
592 let mut encoded = Vec::new();
593 varint::encode_uvarint(7, &mut encoded);
594 varint::encode_varint(0, &mut encoded);
595
596 assert_eq!(
597 Params::decode(&encoded),
598 Err(ProtoError::InvalidEnum {
599 name: "Clock",
600 value: 0,
601 })
602 );
603 assert_eq!(
604 Params::decode_with_presence(&encoded),
605 Err(ProtoError::InvalidEnum {
606 name: "Clock",
607 value: 0,
608 })
609 );
610 }
611
612 #[test]
613 fn explicit_clock_values_one_to_three_are_accepted() {
614 for (value, expected) in [(1, Clock::Wall), (2, Clock::Monotonic), (3, Clock::Both)] {
615 let decoded = Params::decode_with_presence(&encode_int(7, value)).unwrap();
616 assert_eq!(decoded.params.clock, expected);
617 assert!(decoded.presence.clock);
618 }
619 }
620
621 #[test]
622 fn empty_payload_decodes_to_wire_defaults_with_nothing_present() {
623 let decoded = Params::decode_with_presence(&[]).unwrap();
624
625 assert_eq!(decoded.params, Params::default());
626 assert_eq!(decoded.params.protocol_version, 0);
627 assert_eq!(decoded.params.duration_ns, 0);
628 assert_eq!(decoded.params.interval_ns, 0);
629 assert_eq!(decoded.params.length, 0);
630 assert_eq!(decoded.params.dscp, 0);
631 assert_eq!(decoded.params.received_stats, ReceivedStats::None);
632 assert_eq!(decoded.params.stamp_at, StampAt::None);
633 assert_eq!(decoded.params.clock, Clock::Unspecified);
635 assert!(!decoded.params.clock.has_wall());
636 assert!(!decoded.params.clock.has_mono());
637 assert_eq!(decoded.params.server_fill, None);
638
639 assert_eq!(decoded.presence, ParamPresence::default());
640 assert_eq!(
641 decoded.presence,
642 ParamPresence {
643 protocol_version: false,
644 duration_ns: false,
645 interval_ns: false,
646 length: false,
647 received_stats: false,
648 stamp_at: false,
649 clock: false,
650 dscp: false,
651 server_fill: false,
652 }
653 );
654 }
655
656 #[test]
657 fn absent_and_explicitly_zero_parameters_share_a_value_but_not_a_presence() {
658 let absent = Params::decode_with_presence(&encode_int(1, 1)).unwrap();
661 assert_eq!(absent.params.duration_ns, 0);
662 assert!(!absent.presence.duration_ns);
663 assert_eq!(absent.params.interval_ns, 0);
664 assert!(!absent.presence.interval_ns);
665 assert_eq!(absent.params.clock, Clock::Unspecified);
666 assert!(!absent.presence.clock);
667
668 let explicit_duration = Params::decode_with_presence(&encode_int(2, 0)).unwrap();
669 assert_eq!(explicit_duration.params.duration_ns, 0);
670 assert!(explicit_duration.presence.duration_ns);
671
672 let explicit_interval = Params::decode_with_presence(&encode_int(3, 0)).unwrap();
673 assert_eq!(explicit_interval.params.interval_ns, 0);
674 assert!(explicit_interval.presence.interval_ns);
675 }
676
677 #[test]
678 fn every_known_tag_reports_its_own_presence() {
679 let mut encoded = Vec::new();
680 for (tag, value) in [
681 (1, 1),
682 (2, 5),
683 (3, 6),
684 (4, 7),
685 (5, 3),
686 (6, 4),
687 (7, 3),
688 (8, 184),
689 ] {
690 encoded.extend_from_slice(&encode_int(tag, value));
691 }
692 encoded.extend_from_slice(&encode_server_fill_value(b"rand"));
693
694 let decoded = Params::decode_with_presence(&encoded).unwrap();
695 assert_eq!(
696 decoded.presence,
697 ParamPresence {
698 protocol_version: true,
699 duration_ns: true,
700 interval_ns: true,
701 length: true,
702 received_stats: true,
703 stamp_at: true,
704 clock: true,
705 dscp: true,
706 server_fill: true,
707 }
708 );
709 }
710
711 #[test]
712 fn unspecified_clock_is_encoded_by_omission() {
713 let params = Params {
714 protocol_version: 1,
715 clock: Clock::Unspecified,
716 ..Params::default()
717 };
718 let encoded = params.encode();
719
720 assert_eq!(encoded, encode_int(1, 1));
721 assert!(
722 !encoded.windows(2).any(|bytes| bytes[0] == 7),
723 "an unspecified clock must omit the tag rather than encode an explicit zero"
724 );
725 assert_round_trip(params);
726 }
727
728 #[test]
729 fn decode_and_decode_with_presence_agree() {
730 let mut payloads = vec![
731 Vec::new(),
732 encode_int(1, 1),
733 encode_int(2, 0),
734 encode_server_fill_value(b"rand"),
735 ];
736 payloads.push(
737 Params {
738 protocol_version: 1,
739 duration_ns: 3_000_000_000,
740 interval_ns: 1_000_000_000,
741 length: 1472,
742 received_stats: ReceivedStats::Both,
743 stamp_at: StampAt::Both,
744 clock: Clock::Both,
745 dscp: 184,
746 server_fill: Some(ServerFill {
747 value: "rand".to_owned(),
748 }),
749 }
750 .encode(),
751 );
752 payloads.push(vec![1, 0x80]);
754 payloads.push(encode_int(7, 0));
755
756 for payload in payloads {
757 assert_eq!(
758 Params::decode(&payload),
759 Params::decode_with_presence(&payload).map(|decoded| decoded.params),
760 "decode and decode_with_presence disagreed on {payload:02x?}"
761 );
762 }
763 }
764
765 #[test]
766 fn malformed_server_fill_is_rejected() {
767 let encoded = [9, 4, b'a', b'b'];
768 assert_eq!(Params::decode(&encoded), Err(ProtoError::MalformedParams));
769 }
770
771 #[test]
772 fn non_utf8_server_fill_is_rejected() {
773 let encoded = [9, 1, 0xff];
774 assert_eq!(Params::decode(&encoded), Err(ProtoError::InvalidUtf8));
775 }
776
777 #[cfg(target_pointer_width = "32")]
778 #[test]
779 fn server_fill_length_too_large_for_usize_is_rejected() {
780 let mut encoded = Vec::new();
781 varint::encode_uvarint(9, &mut encoded);
782 varint::encode_uvarint(u64::from(u32::MAX) + 1, &mut encoded);
783
784 assert_eq!(
785 Params::decode(&encoded),
786 Err(ProtoError::ParameterLengthTooLarge {
787 tag: 9,
788 length: u64::from(u32::MAX) + 1,
789 })
790 );
791 }
792
793 #[test]
794 fn truncated_varint_parameter_is_rejected() {
795 let encoded = [1, 0x80];
796 assert_eq!(Params::decode(&encoded), Err(ProtoError::TruncatedVarint));
797 }
798
799 #[test]
800 fn truncated_unknown_parameter_value_is_rejected() {
801 let encoded = [99];
802 assert_eq!(Params::decode(&encoded), Err(ProtoError::TruncatedVarint));
803 }
804
805 mod properties {
806 use super::*;
807 use proptest::prelude::*;
808
809 fn received_stats_strategy() -> impl Strategy<Value = ReceivedStats> {
810 prop_oneof![
811 Just(ReceivedStats::None),
812 Just(ReceivedStats::Count),
813 Just(ReceivedStats::Window),
814 Just(ReceivedStats::Both),
815 ]
816 }
817
818 fn stamp_at_strategy() -> impl Strategy<Value = StampAt> {
819 prop_oneof![
820 Just(StampAt::None),
821 Just(StampAt::Send),
822 Just(StampAt::Receive),
823 Just(StampAt::Both),
824 Just(StampAt::Midpoint),
825 ]
826 }
827
828 fn clock_strategy() -> impl Strategy<Value = Clock> {
832 prop_oneof![
833 Just(Clock::Unspecified),
834 Just(Clock::Wall),
835 Just(Clock::Monotonic),
836 Just(Clock::Both),
837 ]
838 }
839
840 fn explicit_clock_value_strategy() -> impl Strategy<Value = i64> {
843 1_i64..=3
844 }
845
846 fn server_fill_strategy() -> impl Strategy<Value = Option<ServerFill>> {
847 prop_oneof![
848 Just(None),
849 proptest::collection::vec(proptest::char::any(), 0..=8).prop_map(|chars| Some(
850 ServerFill {
851 value: chars.into_iter().collect(),
852 }
853 )),
854 ]
855 }
856
857 fn params_strategy() -> impl Strategy<Value = Params> {
858 (
859 any::<i64>(),
860 any::<i64>(),
861 any::<i64>(),
862 any::<i64>(),
863 any::<i64>(),
864 received_stats_strategy(),
865 stamp_at_strategy(),
866 clock_strategy(),
867 server_fill_strategy(),
868 )
869 .prop_map(
870 |(
871 protocol_version,
872 duration_ns,
873 interval_ns,
874 length,
875 dscp,
876 received_stats,
877 stamp_at,
878 clock,
879 server_fill,
880 )| Params {
881 protocol_version,
882 duration_ns,
883 interval_ns,
884 length,
885 received_stats,
886 stamp_at,
887 clock,
888 dscp,
889 server_fill,
890 },
891 )
892 }
893
894 fn encode_tagged_int(tag: u64, value: i64, out: &mut Vec<u8>) {
895 varint::encode_uvarint(tag, out);
896 varint::encode_varint(value, out);
897 }
898
899 fn encode_tagged_server_fill(value: &str, out: &mut Vec<u8>) {
900 varint::encode_uvarint(9, out);
901 varint::encode_uvarint(value.len() as u64, out);
902 out.extend_from_slice(value.as_bytes());
903 }
904
905 proptest! {
906 #[test]
907 fn valid_params_round_trip(params in params_strategy()) {
908 let encoded = params.encode();
909 let decoded = Params::decode(&encoded).unwrap();
910 prop_assert_eq!(decoded, params);
911 }
912
913 #[test]
914 fn encoded_param_presence_matches_omission_rules(params in params_strategy()) {
915 let encoded = params.encode();
916 let decoded = Params::decode_with_presence(&encoded).unwrap();
917 prop_assert_eq!(&decoded.params, ¶ms);
918
919 prop_assert_eq!(decoded.presence.protocol_version, params.protocol_version != 0);
920 prop_assert_eq!(decoded.presence.duration_ns, params.duration_ns != 0);
921 prop_assert_eq!(decoded.presence.interval_ns, params.interval_ns != 0);
922 prop_assert_eq!(decoded.presence.length, params.length != 0);
923 prop_assert_eq!(decoded.presence.dscp, params.dscp != 0);
924 prop_assert_eq!(
925 decoded.presence.received_stats,
926 params.received_stats != ReceivedStats::None
927 );
928 prop_assert_eq!(decoded.presence.stamp_at, params.stamp_at != StampAt::None);
929 prop_assert_eq!(decoded.presence.clock, params.clock != Clock::Unspecified);
930 prop_assert_eq!(decoded.presence.server_fill, params.server_fill.is_some());
931 }
932
933 #[test]
934 fn repeated_protocol_version_is_last_wins(first: i64, second: i64) {
935 let mut encoded = Vec::new();
936 encode_tagged_int(1, first, &mut encoded);
937 encode_tagged_int(1, second, &mut encoded);
938 let decoded = Params::decode_with_presence(&encoded).unwrap();
939 prop_assert_eq!(decoded.params.protocol_version, second);
940 prop_assert!(decoded.presence.protocol_version);
941 }
942
943 #[test]
944 fn repeated_duration_ns_is_last_wins(first: i64, second: i64) {
945 let mut encoded = Vec::new();
946 encode_tagged_int(2, first, &mut encoded);
947 encode_tagged_int(2, second, &mut encoded);
948 let decoded = Params::decode_with_presence(&encoded).unwrap();
949 prop_assert_eq!(decoded.params.duration_ns, second);
950 prop_assert!(decoded.presence.duration_ns);
951 }
952
953 #[test]
954 fn repeated_interval_ns_is_last_wins(first: i64, second: i64) {
955 let mut encoded = Vec::new();
956 encode_tagged_int(3, first, &mut encoded);
957 encode_tagged_int(3, second, &mut encoded);
958 let decoded = Params::decode_with_presence(&encoded).unwrap();
959 prop_assert_eq!(decoded.params.interval_ns, second);
960 prop_assert!(decoded.presence.interval_ns);
961 }
962
963 #[test]
964 fn repeated_length_is_last_wins(first: i64, second: i64) {
965 let mut encoded = Vec::new();
966 encode_tagged_int(4, first, &mut encoded);
967 encode_tagged_int(4, second, &mut encoded);
968 let decoded = Params::decode_with_presence(&encoded).unwrap();
969 prop_assert_eq!(decoded.params.length, second);
970 prop_assert!(decoded.presence.length);
971 }
972
973 #[test]
974 fn repeated_received_stats_is_last_wins(first in 0_i64..=3, second in 0_i64..=3) {
975 let mut encoded = Vec::new();
976 encode_tagged_int(5, first, &mut encoded);
977 encode_tagged_int(5, second, &mut encoded);
978 let decoded = Params::decode_with_presence(&encoded).unwrap();
979 prop_assert_eq!(decoded.params.received_stats, ReceivedStats::try_from(second).unwrap());
980 prop_assert!(decoded.presence.received_stats);
981 }
982
983 #[test]
984 fn repeated_stamp_at_is_last_wins(first in 0_i64..=4, second in 0_i64..=4) {
985 let mut encoded = Vec::new();
986 encode_tagged_int(6, first, &mut encoded);
987 encode_tagged_int(6, second, &mut encoded);
988 let decoded = Params::decode_with_presence(&encoded).unwrap();
989 prop_assert_eq!(decoded.params.stamp_at, StampAt::try_from(second).unwrap());
990 prop_assert!(decoded.presence.stamp_at);
991 }
992
993 #[test]
994 fn repeated_clock_is_last_wins(
995 first in explicit_clock_value_strategy(),
996 second in explicit_clock_value_strategy(),
997 ) {
998 let mut encoded = Vec::new();
999 encode_tagged_int(7, first, &mut encoded);
1000 encode_tagged_int(7, second, &mut encoded);
1001 let decoded = Params::decode_with_presence(&encoded).unwrap();
1002 prop_assert_eq!(decoded.params.clock, Clock::try_from(second).unwrap());
1003 prop_assert!(decoded.presence.clock);
1004 }
1005
1006 #[test]
1007 fn repeated_dscp_is_last_wins(first: i64, second: i64) {
1008 let mut encoded = Vec::new();
1009 encode_tagged_int(8, first, &mut encoded);
1010 encode_tagged_int(8, second, &mut encoded);
1011 let decoded = Params::decode_with_presence(&encoded).unwrap();
1012 prop_assert_eq!(decoded.params.dscp, second);
1013 prop_assert!(decoded.presence.dscp);
1014 }
1015
1016 #[test]
1017 fn repeated_server_fill_is_last_wins(
1018 first in proptest::collection::vec(proptest::char::any(), 0..=8)
1019 .prop_map(|chars| chars.into_iter().collect::<String>()),
1020 second in proptest::collection::vec(proptest::char::any(), 0..=8)
1021 .prop_map(|chars| chars.into_iter().collect::<String>()),
1022 ) {
1023 let mut encoded = Vec::new();
1024 encode_tagged_server_fill(&first, &mut encoded);
1025 encode_tagged_server_fill(&second, &mut encoded);
1026 let decoded = Params::decode_with_presence(&encoded).unwrap();
1027 prop_assert_eq!(
1028 decoded.params.server_fill,
1029 Some(ServerFill { value: second })
1030 );
1031 prop_assert!(decoded.presence.server_fill);
1032 }
1033
1034 #[test]
1035 fn unknown_scalar_tag_prefix_does_not_disturb_known_params(
1036 params in params_strategy(),
1037 unknown_tag in 10_u64..=u16::MAX as u64,
1038 unknown_value: u64,
1039 ) {
1040 let mut prefixed = Vec::new();
1041 varint::encode_uvarint(unknown_tag, &mut prefixed);
1042 varint::encode_uvarint(unknown_value, &mut prefixed);
1043 prefixed.extend_from_slice(¶ms.encode());
1044
1045 let decoded = Params::decode(&prefixed).unwrap();
1046 prop_assert_eq!(decoded, params.clone());
1047
1048 let decoded_with_presence = Params::decode_with_presence(&prefixed).unwrap();
1049 let expected_presence = Params::decode_with_presence(¶ms.encode()).unwrap().presence;
1050 prop_assert_eq!(decoded_with_presence.presence, expected_presence);
1051 }
1052
1053 #[test]
1054 fn unknown_scalar_tag_suffix_does_not_disturb_known_params(
1055 params in params_strategy(),
1056 unknown_tag in 10_u64..=u16::MAX as u64,
1057 unknown_value: u64,
1058 ) {
1059 let mut suffixed = params.encode();
1060 varint::encode_uvarint(unknown_tag, &mut suffixed);
1061 varint::encode_uvarint(unknown_value, &mut suffixed);
1062
1063 let decoded = Params::decode(&suffixed).unwrap();
1064 prop_assert_eq!(decoded, params.clone());
1065
1066 let decoded_with_presence = Params::decode_with_presence(&suffixed).unwrap();
1067 let expected_presence = Params::decode_with_presence(¶ms.encode()).unwrap().presence;
1068 prop_assert_eq!(decoded_with_presence.presence, expected_presence);
1069 }
1070 }
1071
1072 #[test]
1077 fn omitted_vs_explicit_zero_presence_for_zero_valid_fields() {
1078 let scalar_tags = [1_u64, 2, 3, 4, 8];
1079 for tag in scalar_tags {
1080 let omitted = Params::decode_with_presence(&[]).unwrap();
1081 let mut explicit_zero = Vec::new();
1082 encode_tagged_int(tag, 0, &mut explicit_zero);
1083 let explicit = Params::decode_with_presence(&explicit_zero).unwrap();
1084
1085 assert_eq!(omitted.params, explicit.params);
1086 assert_eq!(explicit.params, Params::default());
1087
1088 let (omitted_presence, explicit_presence) = match tag {
1089 1 => (
1090 omitted.presence.protocol_version,
1091 explicit.presence.protocol_version,
1092 ),
1093 2 => (omitted.presence.duration_ns, explicit.presence.duration_ns),
1094 3 => (omitted.presence.interval_ns, explicit.presence.interval_ns),
1095 4 => (omitted.presence.length, explicit.presence.length),
1096 8 => (omitted.presence.dscp, explicit.presence.dscp),
1097 _ => unreachable!(),
1098 };
1099 assert!(!omitted_presence);
1100 assert!(explicit_presence);
1101 }
1102
1103 let enum_tags = [5_u64, 6];
1104 for tag in enum_tags {
1105 let omitted = Params::decode_with_presence(&[]).unwrap();
1106 let mut explicit_zero = Vec::new();
1107 encode_tagged_int(tag, 0, &mut explicit_zero);
1108 let explicit = Params::decode_with_presence(&explicit_zero).unwrap();
1109
1110 assert_eq!(omitted.params, explicit.params);
1111
1112 let (omitted_presence, explicit_presence) = match tag {
1113 5 => (
1114 omitted.presence.received_stats,
1115 explicit.presence.received_stats,
1116 ),
1117 6 => (omitted.presence.stamp_at, explicit.presence.stamp_at),
1118 _ => unreachable!(),
1119 };
1120 assert!(!omitted_presence);
1121 assert!(explicit_presence);
1122 }
1123
1124 let omitted = Params::decode_with_presence(&[]).unwrap();
1127 assert_eq!(omitted.params.clock, Clock::Unspecified);
1128 assert!(!omitted.presence.clock);
1129 let mut explicit_clock_zero = Vec::new();
1130 encode_tagged_int(7, 0, &mut explicit_clock_zero);
1131 assert_eq!(
1132 Params::decode_with_presence(&explicit_clock_zero),
1133 Err(ProtoError::InvalidEnum {
1134 name: "Clock",
1135 value: 0,
1136 })
1137 );
1138 }
1139 }
1140}