1use byteorder::{BE, ReadBytesExt, WriteBytesExt};
2use std::io;
3
4#[cfg(feature = "ntpv5")]
5use super::ntpv5::{NtpV5Flags, PacketV5, Time32, Timescale};
6use super::{
7 DateFormat, KissOfDeath, LeapIndicator, Mode, Packet, PrimarySource, ReadBytes, ReadFromBytes,
8 ReferenceIdentifier, ShortFormat, Stratum, TimestampFormat, Version, WriteBytes, WriteToBytes,
9 be_u32_to_bytes,
10};
11use crate::error::ParseError;
12
13impl<W> WriteBytes for W
16where
17 W: WriteBytesExt,
18{
19 fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()> {
20 protocol.write_to_bytes(self)
21 }
22}
23
24impl<P> WriteToBytes for &P
25where
26 P: WriteToBytes,
27{
28 fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()> {
29 (*self).write_to_bytes(writer)
30 }
31}
32
33impl WriteToBytes for ShortFormat {
34 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
35 writer.write_u16::<BE>(self.seconds)?;
36 writer.write_u16::<BE>(self.fraction)?;
37 Ok(())
38 }
39}
40
41impl WriteToBytes for TimestampFormat {
42 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
43 writer.write_u32::<BE>(self.seconds)?;
44 writer.write_u32::<BE>(self.fraction)?;
45 Ok(())
46 }
47}
48
49impl WriteToBytes for DateFormat {
50 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
51 writer.write_i32::<BE>(self.era_number)?;
52 writer.write_u32::<BE>(self.era_offset)?;
53 writer.write_u64::<BE>(self.fraction)?;
54 Ok(())
55 }
56}
57
58impl WriteToBytes for Stratum {
59 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
60 writer.write_u8(self.0)?;
61 Ok(())
62 }
63}
64
65impl WriteToBytes for ReferenceIdentifier {
66 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
67 match *self {
68 ReferenceIdentifier::KissOfDeath(kod) => {
69 writer.write_u32::<BE>(kod as u32)?;
70 }
71 ReferenceIdentifier::PrimarySource(src) => {
72 writer.write_u32::<BE>(src as u32)?;
73 }
74 ReferenceIdentifier::SecondaryOrClient(arr) => {
75 writer.write_u32::<BE>(code_to_u32!(&arr))?;
76 }
77 ReferenceIdentifier::Unknown(arr) => {
78 writer.write_u32::<BE>(code_to_u32!(&arr))?;
79 }
80 }
81 Ok(())
82 }
83}
84
85impl WriteToBytes for (LeapIndicator, Version, Mode) {
86 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
87 let (li, vn, mode) = *self;
88 let mut li_vn_mode = 0;
89 li_vn_mode |= (li as u8) << 6;
90 li_vn_mode |= vn.0 << 3;
91 li_vn_mode |= mode as u8;
92 writer.write_u8(li_vn_mode)?;
93 Ok(())
94 }
95}
96
97impl WriteToBytes for Packet {
98 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
99 let li_vn_mode = (self.leap_indicator, self.version, self.mode);
100 writer.write_bytes(li_vn_mode)?;
101 writer.write_bytes(self.stratum)?;
102 writer.write_i8(self.poll)?;
103 writer.write_i8(self.precision)?;
104 writer.write_bytes(self.root_delay)?;
105 writer.write_bytes(self.root_dispersion)?;
106 writer.write_bytes(self.reference_id)?;
107 writer.write_bytes(self.reference_timestamp)?;
108 writer.write_bytes(self.origin_timestamp)?;
109 writer.write_bytes(self.receive_timestamp)?;
110 writer.write_bytes(self.transmit_timestamp)?;
111 Ok(())
112 }
113}
114
115impl<R> ReadBytes for R
118where
119 R: ReadBytesExt,
120{
121 fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P> {
122 P::read_from_bytes(self)
123 }
124}
125
126impl ReadFromBytes for ShortFormat {
127 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
128 let seconds = reader.read_u16::<BE>()?;
129 let fraction = reader.read_u16::<BE>()?;
130 let short_format = ShortFormat { seconds, fraction };
131 Ok(short_format)
132 }
133}
134
135impl ReadFromBytes for TimestampFormat {
136 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
137 let seconds = reader.read_u32::<BE>()?;
138 let fraction = reader.read_u32::<BE>()?;
139 let timestamp_format = TimestampFormat { seconds, fraction };
140 Ok(timestamp_format)
141 }
142}
143
144impl ReadFromBytes for DateFormat {
145 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
146 let era_number = reader.read_i32::<BE>()?;
147 let era_offset = reader.read_u32::<BE>()?;
148 let fraction = reader.read_u64::<BE>()?;
149 let date_format = DateFormat {
150 era_number,
151 era_offset,
152 fraction,
153 };
154 Ok(date_format)
155 }
156}
157
158impl ReadFromBytes for Stratum {
159 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
160 let stratum = Stratum(reader.read_u8()?);
161 Ok(stratum)
162 }
163}
164
165impl ReadFromBytes for (LeapIndicator, Version, Mode) {
166 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
167 let li_vn_mode = reader.read_u8()?;
168 let li_u8 = li_vn_mode >> 6;
169 let vn_u8 = (li_vn_mode >> 3) & 0b111;
170 let mode_u8 = li_vn_mode & 0b111;
171 let li = LeapIndicator::try_from(li_u8).map_err(|_| ParseError::InvalidField {
172 field: "leap indicator",
173 value: li_u8 as u32,
174 })?;
175 let vn = Version(vn_u8);
176 let mode = Mode::try_from(mode_u8).map_err(|_| ParseError::InvalidField {
177 field: "mode",
178 value: mode_u8 as u32,
179 })?;
180 Ok((li, vn, mode))
181 }
182}
183
184impl ReadFromBytes for Packet {
185 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
186 let (leap_indicator, version, mode) = reader.read_bytes()?;
187 let stratum = reader.read_bytes::<Stratum>()?;
188 let poll = reader.read_i8()?;
189 let precision = reader.read_i8()?;
190 let root_delay = reader.read_bytes()?;
191 let root_dispersion = reader.read_bytes()?;
192 let reference_id = {
193 let u = reader.read_u32::<BE>()?;
194 let raw_bytes = be_u32_to_bytes(u);
195 if stratum == Stratum::UNSPECIFIED {
196 match KissOfDeath::try_from(u) {
198 Ok(kod) => ReferenceIdentifier::KissOfDeath(kod),
199 Err(_) => ReferenceIdentifier::Unknown(raw_bytes),
200 }
201 } else if stratum == Stratum::PRIMARY {
202 match PrimarySource::try_from(u) {
204 Ok(src) => ReferenceIdentifier::PrimarySource(src),
205 Err(_) => ReferenceIdentifier::Unknown(raw_bytes),
206 }
207 } else if stratum.is_secondary() {
208 ReferenceIdentifier::SecondaryOrClient(raw_bytes)
210 } else {
211 ReferenceIdentifier::Unknown(raw_bytes)
213 }
214 };
215 let reference_timestamp = reader.read_bytes()?;
216 let origin_timestamp = reader.read_bytes()?;
217 let receive_timestamp = reader.read_bytes()?;
218 let transmit_timestamp = reader.read_bytes()?;
219 Ok(Packet {
220 leap_indicator,
221 version,
222 mode,
223 stratum,
224 poll,
225 precision,
226 root_delay,
227 root_dispersion,
228 reference_id,
229 reference_timestamp,
230 origin_timestamp,
231 receive_timestamp,
232 transmit_timestamp,
233 })
234 }
235}
236
237#[cfg(feature = "ntpv5")]
242impl WriteToBytes for Time32 {
243 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
244 writer.write_u32::<BE>(self.0)?;
245 Ok(())
246 }
247}
248
249#[cfg(feature = "ntpv5")]
250impl ReadFromBytes for Time32 {
251 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
252 let raw = reader.read_u32::<BE>()?;
253 Ok(Time32(raw))
254 }
255}
256
257#[cfg(feature = "ntpv5")]
258impl WriteToBytes for Timescale {
259 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
260 writer.write_u8(*self as u8)?;
261 Ok(())
262 }
263}
264
265#[cfg(feature = "ntpv5")]
266impl ReadFromBytes for Timescale {
267 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
268 let raw = reader.read_u8()?;
269 Timescale::try_from(raw).map_err(|_| {
270 ParseError::InvalidField {
271 field: "timescale",
272 value: raw as u32,
273 }
274 .into()
275 })
276 }
277}
278
279#[cfg(feature = "ntpv5")]
280impl WriteToBytes for NtpV5Flags {
281 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
282 writer.write_u16::<BE>(self.0)?;
283 Ok(())
284 }
285}
286
287#[cfg(feature = "ntpv5")]
288impl ReadFromBytes for NtpV5Flags {
289 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
290 let raw = reader.read_u16::<BE>()?;
291 Ok(NtpV5Flags(raw))
292 }
293}
294
295#[cfg(feature = "ntpv5")]
296impl WriteToBytes for PacketV5 {
297 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
298 let li_vn_mode = (self.leap_indicator, self.version, self.mode);
299 writer.write_bytes(li_vn_mode)?;
300 writer.write_bytes(self.stratum)?;
301 writer.write_i8(self.poll)?;
302 writer.write_i8(self.precision)?;
303 writer.write_bytes(self.root_delay)?;
304 writer.write_bytes(self.root_dispersion)?;
305 writer.write_bytes(self.timescale)?;
306 writer.write_u8(self.era)?;
307 writer.write_bytes(self.flags)?;
308 writer.write_u64::<BE>(self.server_cookie)?;
309 writer.write_u64::<BE>(self.client_cookie)?;
310 writer.write_bytes(self.receive_timestamp)?;
311 writer.write_bytes(self.transmit_timestamp)?;
312 Ok(())
313 }
314}
315
316#[cfg(feature = "ntpv5")]
317impl ReadFromBytes for PacketV5 {
318 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
319 let (leap_indicator, version, mode) = reader.read_bytes()?;
320 let stratum = reader.read_bytes::<Stratum>()?;
321 let poll = reader.read_i8()?;
322 let precision = reader.read_i8()?;
323 let root_delay = reader.read_bytes::<Time32>()?;
324 let root_dispersion = reader.read_bytes::<Time32>()?;
325 let timescale = reader.read_bytes::<Timescale>()?;
326 let era = reader.read_u8()?;
327 let flags = reader.read_bytes::<NtpV5Flags>()?;
328 let server_cookie = reader.read_u64::<BE>()?;
329 let client_cookie = reader.read_u64::<BE>()?;
330 let receive_timestamp = reader.read_bytes()?;
331 let transmit_timestamp = reader.read_bytes()?;
332 Ok(PacketV5 {
333 leap_indicator,
334 version,
335 mode,
336 stratum,
337 poll,
338 precision,
339 root_delay,
340 root_dispersion,
341 timescale,
342 era,
343 flags,
344 server_cookie,
345 client_cookie,
346 receive_timestamp,
347 transmit_timestamp,
348 })
349 }
350}
351
352#[cfg(all(test, feature = "std"))]
357mod tests {
358 use super::*;
359 use std::io::Cursor;
360
361 #[test]
364 fn short_format_roundtrip() {
365 let sf = ShortFormat {
366 seconds: 0x1234,
367 fraction: 0x5678,
368 };
369 let mut buf = Vec::new();
370 buf.write_bytes(sf).unwrap();
371 assert_eq!(buf.len(), 4);
372 let decoded: ShortFormat = Cursor::new(&buf).read_bytes().unwrap();
373 assert_eq!(decoded.seconds, sf.seconds);
374 assert_eq!(decoded.fraction, sf.fraction);
375 }
376
377 #[test]
378 fn short_format_edge_values() {
379 for (s, f) in [(0u16, 0u16), (u16::MAX, u16::MAX)] {
380 let sf = ShortFormat {
381 seconds: s,
382 fraction: f,
383 };
384 let mut buf = Vec::new();
385 buf.write_bytes(sf).unwrap();
386 let decoded: ShortFormat = Cursor::new(&buf).read_bytes().unwrap();
387 assert_eq!(decoded.seconds, s);
388 assert_eq!(decoded.fraction, f);
389 }
390 }
391
392 #[test]
393 fn short_format_read_too_short() {
394 let buf = [0u8; 3];
395 let result = Cursor::new(&buf[..]).read_bytes::<ShortFormat>();
396 assert!(result.is_err());
397 }
398
399 #[test]
402 fn timestamp_format_roundtrip() {
403 let ts = TimestampFormat {
404 seconds: 3_913_056_000,
405 fraction: 0xABCD_1234,
406 };
407 let mut buf = Vec::new();
408 buf.write_bytes(ts).unwrap();
409 assert_eq!(buf.len(), 8);
410 let decoded: TimestampFormat = Cursor::new(&buf).read_bytes().unwrap();
411 assert_eq!(decoded.seconds, ts.seconds);
412 assert_eq!(decoded.fraction, ts.fraction);
413 }
414
415 #[test]
416 fn timestamp_format_edge_values() {
417 for (s, f) in [(0u32, 0u32), (u32::MAX, u32::MAX)] {
418 let ts = TimestampFormat {
419 seconds: s,
420 fraction: f,
421 };
422 let mut buf = Vec::new();
423 buf.write_bytes(ts).unwrap();
424 let decoded: TimestampFormat = Cursor::new(&buf).read_bytes().unwrap();
425 assert_eq!(decoded.seconds, s);
426 assert_eq!(decoded.fraction, f);
427 }
428 }
429
430 #[test]
431 fn timestamp_format_read_too_short() {
432 let buf = [0u8; 7];
433 let result = Cursor::new(&buf[..]).read_bytes::<TimestampFormat>();
434 assert!(result.is_err());
435 }
436
437 #[test]
440 fn date_format_roundtrip() {
441 let df = DateFormat {
442 era_number: -1,
443 era_offset: 0x1234_5678,
444 fraction: 0xDEAD_BEEF_CAFE_BABE,
445 };
446 let mut buf = Vec::new();
447 buf.write_bytes(df).unwrap();
448 assert_eq!(buf.len(), 16);
449 let decoded: DateFormat = Cursor::new(&buf).read_bytes().unwrap();
450 assert_eq!(decoded.era_number, df.era_number);
451 assert_eq!(decoded.era_offset, df.era_offset);
452 assert_eq!(decoded.fraction, df.fraction);
453 }
454
455 #[test]
456 fn date_format_read_too_short() {
457 let buf = [0u8; 15];
458 let result = Cursor::new(&buf[..]).read_bytes::<DateFormat>();
459 assert!(result.is_err());
460 }
461
462 #[test]
465 fn stratum_roundtrip() {
466 for val in [0u8, 1, 2, 15, 16, 255] {
467 let s = Stratum(val);
468 let mut buf = Vec::new();
469 buf.write_bytes(s).unwrap();
470 assert_eq!(buf.len(), 1);
471 let decoded: Stratum = Cursor::new(&buf).read_bytes().unwrap();
472 assert_eq!(decoded.0, val);
473 }
474 }
475
476 #[test]
477 fn stratum_read_empty() {
478 let buf: [u8; 0] = [];
479 let result = Cursor::new(&buf[..]).read_bytes::<Stratum>();
480 assert!(result.is_err());
481 }
482
483 #[test]
486 fn li_vn_mode_roundtrip() {
487 let li = LeapIndicator::NoWarning;
488 let vn = Version::V4;
489 let mode = Mode::Client;
490 let mut buf = Vec::new();
491 buf.write_bytes((li, vn, mode)).unwrap();
492 assert_eq!(buf.len(), 1);
493 let (dli, dvn, dmode): (LeapIndicator, Version, Mode) =
494 Cursor::new(&buf).read_bytes().unwrap();
495 assert_eq!(dli, li);
496 assert_eq!(dvn, vn);
497 assert_eq!(dmode, mode);
498 }
499
500 #[test]
501 fn li_vn_mode_all_leap_indicators() {
502 for li in [
503 LeapIndicator::NoWarning,
504 LeapIndicator::AddOne,
505 LeapIndicator::SubOne,
506 LeapIndicator::Unknown,
507 ] {
508 let mut buf = Vec::new();
509 buf.write_bytes((li, Version::V4, Mode::Server)).unwrap();
510 let (dli, _, _): (LeapIndicator, Version, Mode) =
511 Cursor::new(&buf).read_bytes().unwrap();
512 assert_eq!(dli, li);
513 }
514 }
515
516 #[test]
517 fn li_vn_mode_all_modes() {
518 for mode in [
519 Mode::Reserved,
520 Mode::SymmetricActive,
521 Mode::SymmetricPassive,
522 Mode::Client,
523 Mode::Server,
524 Mode::Broadcast,
525 Mode::NtpControlMessage,
526 Mode::ReservedForPrivateUse,
527 ] {
528 let mut buf = Vec::new();
529 buf.write_bytes((LeapIndicator::NoWarning, Version::V4, mode))
530 .unwrap();
531 let (_, _, dm): (LeapIndicator, Version, Mode) =
532 Cursor::new(&buf).read_bytes().unwrap();
533 assert_eq!(dm, mode);
534 }
535 }
536
537 #[test]
538 fn li_vn_mode_read_empty() {
539 let buf: [u8; 0] = [];
540 let result = Cursor::new(&buf[..]).read_bytes::<(LeapIndicator, Version, Mode)>();
541 assert!(result.is_err());
542 }
543
544 #[test]
547 fn reference_id_primary_source_roundtrip() {
548 let ref_id = ReferenceIdentifier::PrimarySource(PrimarySource::Gps);
549 let mut buf = Vec::new();
550 buf.write_bytes(ref_id).unwrap();
551 assert_eq!(buf.len(), 4);
552 }
553
554 #[test]
555 fn reference_id_kiss_of_death_roundtrip() {
556 let ref_id = ReferenceIdentifier::KissOfDeath(KissOfDeath::Deny);
557 let mut buf = Vec::new();
558 buf.write_bytes(ref_id).unwrap();
559 assert_eq!(buf.len(), 4);
560 }
561
562 #[test]
563 fn reference_id_secondary_roundtrip() {
564 let ref_id = ReferenceIdentifier::SecondaryOrClient([192, 168, 1, 1]);
565 let mut buf = Vec::new();
566 buf.write_bytes(ref_id).unwrap();
567 assert_eq!(buf, [192, 168, 1, 1]);
568 }
569
570 fn make_test_packet() -> Packet {
573 Packet {
574 leap_indicator: LeapIndicator::NoWarning,
575 version: Version::V4,
576 mode: Mode::Client,
577 stratum: Stratum::UNSPECIFIED,
578 poll: 6,
579 precision: -20,
580 root_delay: ShortFormat {
581 seconds: 1,
582 fraction: 0x8000,
583 },
584 root_dispersion: ShortFormat {
585 seconds: 0,
586 fraction: 0x4000,
587 },
588 reference_id: ReferenceIdentifier::default(),
589 reference_timestamp: TimestampFormat {
590 seconds: 3_913_056_000,
591 fraction: 0,
592 },
593 origin_timestamp: TimestampFormat::default(),
594 receive_timestamp: TimestampFormat::default(),
595 transmit_timestamp: TimestampFormat {
596 seconds: 3_913_056_001,
597 fraction: 0x1234_5678,
598 },
599 }
600 }
601
602 #[test]
603 fn packet_roundtrip() {
604 let pkt = make_test_packet();
605 let mut buf = Vec::new();
606 buf.write_bytes(pkt).unwrap();
607 assert_eq!(buf.len(), 48);
608 let decoded: Packet = Cursor::new(&buf).read_bytes().unwrap();
609 assert_eq!(decoded.leap_indicator, pkt.leap_indicator);
610 assert_eq!(decoded.version, pkt.version);
611 assert_eq!(decoded.mode, pkt.mode);
612 assert_eq!(decoded.stratum, pkt.stratum);
613 assert_eq!(decoded.poll, pkt.poll);
614 assert_eq!(decoded.precision, pkt.precision);
615 assert_eq!(decoded.root_delay, pkt.root_delay);
616 assert_eq!(decoded.root_dispersion, pkt.root_dispersion);
617 assert_eq!(decoded.reference_timestamp, pkt.reference_timestamp);
618 assert_eq!(decoded.origin_timestamp, pkt.origin_timestamp);
619 assert_eq!(decoded.receive_timestamp, pkt.receive_timestamp);
620 assert_eq!(decoded.transmit_timestamp, pkt.transmit_timestamp);
621 }
622
623 #[test]
624 fn packet_read_too_short() {
625 let buf = [0u8; 47];
626 let result = Cursor::new(&buf[..]).read_bytes::<Packet>();
627 assert!(result.is_err());
628 }
629
630 #[test]
631 fn packet_stratum1_gps_reference() {
632 let pkt = Packet {
633 stratum: Stratum::PRIMARY,
634 reference_id: ReferenceIdentifier::PrimarySource(PrimarySource::Gps),
635 ..make_test_packet()
636 };
637 let mut buf = Vec::new();
638 buf.write_bytes(pkt).unwrap();
639 let decoded: Packet = Cursor::new(&buf).read_bytes().unwrap();
640 assert!(matches!(
641 decoded.reference_id,
642 ReferenceIdentifier::PrimarySource(PrimarySource::Gps)
643 ));
644 }
645
646 #[test]
647 fn packet_stratum0_kod_deny() {
648 let pkt = Packet {
649 stratum: Stratum::UNSPECIFIED,
650 reference_id: ReferenceIdentifier::KissOfDeath(KissOfDeath::Deny),
651 ..make_test_packet()
652 };
653 let mut buf = Vec::new();
654 buf.write_bytes(pkt).unwrap();
655 let decoded: Packet = Cursor::new(&buf).read_bytes().unwrap();
656 assert!(matches!(
657 decoded.reference_id,
658 ReferenceIdentifier::KissOfDeath(KissOfDeath::Deny)
659 ));
660 }
661
662 #[test]
663 fn packet_stratum2_secondary_reference() {
664 let pkt = Packet {
665 stratum: Stratum(2),
666 reference_id: ReferenceIdentifier::SecondaryOrClient([10, 0, 0, 1]),
667 ..make_test_packet()
668 };
669 let mut buf = Vec::new();
670 buf.write_bytes(pkt).unwrap();
671 let decoded: Packet = Cursor::new(&buf).read_bytes().unwrap();
672 assert!(matches!(
673 decoded.reference_id,
674 ReferenceIdentifier::SecondaryOrClient([10, 0, 0, 1])
675 ));
676 }
677
678 #[test]
679 fn packet_stratum16_unknown_reference() {
680 let pkt = Packet {
681 stratum: Stratum(16),
682 reference_id: ReferenceIdentifier::Unknown([0xFF, 0xFE, 0xFD, 0xFC]),
683 ..make_test_packet()
684 };
685 let mut buf = Vec::new();
686 buf.write_bytes(pkt).unwrap();
687 let decoded: Packet = Cursor::new(&buf).read_bytes().unwrap();
688 assert!(matches!(
689 decoded.reference_id,
690 ReferenceIdentifier::Unknown([0xFF, 0xFE, 0xFD, 0xFC])
691 ));
692 }
693
694 #[test]
695 fn packet_negative_poll_precision() {
696 let pkt = Packet {
697 poll: -6,
698 precision: -32,
699 ..make_test_packet()
700 };
701 let mut buf = Vec::new();
702 buf.write_bytes(pkt).unwrap();
703 let decoded: Packet = Cursor::new(&buf).read_bytes().unwrap();
704 assert_eq!(decoded.poll, -6);
705 assert_eq!(decoded.precision, -32);
706 }
707
708 #[test]
709 fn packet_reference_write_is_big_endian() {
710 let pkt = make_test_packet();
711 let mut buf = Vec::new();
712 buf.write_bytes(pkt).unwrap();
713 assert_eq!(buf[0], 0x23);
715 }
716}