1use crate::InterfaceId;
2use alloc::vec::Vec;
3use core::num::NonZeroU8;
4use parity_scale_codec::{Decode, Encode, Error, Input, Output};
5
6pub const HIGHEST_SUPPORTED_VERSION: u8 = 1;
8
9pub const MAGIC_BYTES: [u8; 2] = [0x47, 0x4D];
12
13pub const MINIMAL_HLEN: u8 = 16;
15
16#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct SailsMessageHeader {
22 version: Version,
23 hlen: HeaderLength,
24 interface_id: InterfaceId,
25 route_id: u8,
26 entry_id: u16,
27}
28
29impl SailsMessageHeader {
30 pub const fn new(
32 version: Version,
33 hlen: HeaderLength,
34 interface_id: InterfaceId,
35 route_id: u8,
36 entry_id: u16,
37 ) -> Self {
38 Self {
39 version,
40 hlen,
41 interface_id,
42 route_id,
43 entry_id,
44 }
45 }
46
47 pub const fn v1(interface_id: InterfaceId, entry_id: u16, route_id: u8) -> Self {
48 Self {
49 version: Version::v1(),
50 hlen: HeaderLength(MINIMAL_HLEN),
51 interface_id,
52 route_id,
53 entry_id,
54 }
55 }
56
57 pub const fn version(&self) -> Version {
59 self.version
60 }
61
62 pub const fn hlen(&self) -> HeaderLength {
64 self.hlen
65 }
66
67 pub const fn interface_id(&self) -> InterfaceId {
69 self.interface_id
70 }
71
72 pub const fn route_id(&self) -> u8 {
74 self.route_id
75 }
76
77 pub const fn entry_id(&self) -> u16 {
79 self.entry_id
80 }
81}
82
83impl SailsMessageHeader {
85 pub fn to_bytes(&self) -> Vec<u8> {
87 assert!(
88 self.hlen.inner() == MINIMAL_HLEN,
89 "encoding headers with extensions is not supported yet"
90 );
91 let mut bytes = Vec::with_capacity(self.hlen.inner() as usize);
92 bytes.extend_from_slice(Magic::new().as_bytes());
93 bytes.push(self.version.inner());
94 bytes.push(self.hlen.inner());
95 bytes.extend_from_slice(self.interface_id.as_bytes());
96 bytes.extend_from_slice(&self.entry_id.to_le_bytes());
97 bytes.push(self.route_id);
98 bytes.push(0);
100
101 bytes
102 }
103
104 pub fn try_read_bytes(bytes: &mut &[u8]) -> Result<Self, &'static str> {
106 let input_len = bytes.len();
107 if input_len < MINIMAL_HLEN as usize {
108 return Err("Insufficient bytes for header");
109 }
110
111 Magic::try_read_bytes(bytes)?;
113
114 let version = Version::try_read_bytes(bytes)?;
115 let hlen = HeaderLength::try_read_bytes(bytes)?;
116 if version == Version::v1() && hlen.0 > MINIMAL_HLEN {
117 return Err("Header len must be 16 in version 1");
118 }
119 let interface_id = InterfaceId::try_read_bytes(bytes)?;
120
121 let entry_id = u16::from_le_bytes([bytes[0], bytes[1]]);
122 let route_id = bytes[2];
123 let reserved = bytes[3];
124
125 if version == Version::v1() && reserved != 0 {
126 return Err("Reserved byte must be zero in version 1");
127 }
128
129 *bytes = &bytes[4..];
131
132 Ok(Self {
133 version,
134 hlen,
135 interface_id,
136 route_id,
137 entry_id,
138 })
139 }
140
141 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
143 let mut slice = bytes;
144 Self::try_read_bytes(&mut slice)
145 }
146
147 pub fn try_match_interfaces(
152 self,
153 interfaces: &[(InterfaceId, u8)],
154 ) -> Result<MatchedInterface, &'static str> {
155 let Self {
156 interface_id,
157 route_id,
158 entry_id,
159 ..
160 } = self;
161
162 let message_route = NonZeroU8::new(route_id);
163 let mut resolved_route = None;
164
165 for (_, program_route_id) in interfaces.iter().filter(|(id, _)| *id == interface_id) {
166 let program_route_id = match NonZeroU8::new(*program_route_id) {
167 Some(route) => route,
168 None => return Err("Interfaces must not contain route ID `0`"),
169 };
170 if message_route.is_some_and(|id| id == program_route_id) {
172 resolved_route = Some(program_route_id);
173 break;
174 }
175 if message_route.is_none() && resolved_route.replace(program_route_id).is_some() {
177 return Err("Can't infer the interface by route ID `0`, many instances");
178 }
179 }
180 match resolved_route {
181 Some(route) => Ok(MatchedInterface {
182 interface_id,
183 entry_id,
184 route_id: route.get(),
185 }),
186 None => Err("No matching interface and route ID found"),
187 }
188 }
189}
190
191impl Encode for SailsMessageHeader {
192 fn encode_to<O: Output + ?Sized>(&self, dest: &mut O) {
193 assert!(
194 self.hlen.inner() == MINIMAL_HLEN,
195 "encoding headers with extensions is not supported yet"
196 );
197 dest.write(Magic::new().as_bytes());
199 dest.push_byte(self.version.inner());
200 dest.push_byte(self.hlen.inner());
201 dest.write(self.interface_id.as_bytes());
202 dest.write(&self.entry_id.to_le_bytes());
203 dest.push_byte(self.route_id);
204 dest.push_byte(0);
206 }
207}
208
209impl Decode for SailsMessageHeader {
210 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
211 let mut header_bytes = [0u8; MINIMAL_HLEN as usize]; input.read(&mut header_bytes)?;
213
214 let mut slice = header_bytes.as_slice();
215 Self::try_read_bytes(&mut slice).map_err(Error::from)
216 }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode)]
221pub struct Magic([u8; 2]);
222
223impl Magic {
224 #[allow(clippy::new_without_default)]
225 pub fn new() -> Self {
226 Self(MAGIC_BYTES)
227 }
228
229 pub fn as_bytes(&self) -> &[u8] {
231 &self.0
232 }
233
234 pub fn try_read_bytes(bytes: &mut &[u8]) -> Result<Self, &'static str> {
236 if bytes.len() < MAGIC_BYTES.len() {
237 return Err("Insufficient bytes for magic");
238 }
239
240 let magic = [bytes[0], bytes[1]];
241 if magic != MAGIC_BYTES {
242 return Err("Invalid Sails magic bytes");
243 }
244
245 *bytes = &bytes[2..];
246 Ok(Self(magic))
247 }
248
249 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
251 let mut slice = bytes;
252 Self::try_read_bytes(&mut slice)
253 }
254}
255
256impl Decode for Magic {
257 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
258 let mut magic = [0u8; 2];
259 input.read(&mut magic)?;
260
261 let mut slice = magic.as_slice();
262 Self::try_read_bytes(&mut slice).map_err(Error::from)
263 }
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode)]
268pub struct Version(u8);
269
270impl Version {
271 pub const fn v1() -> Self {
273 Self(1)
274 }
275
276 pub const fn latest() -> Self {
278 Self(HIGHEST_SUPPORTED_VERSION)
279 }
280
281 pub fn new(version: u8) -> Result<Self, &'static str> {
287 if version == 0 || version > HIGHEST_SUPPORTED_VERSION {
288 Err("Unsupported Sails version")
289 } else {
290 Ok(Self(version))
291 }
292 }
293
294 pub fn inner(&self) -> u8 {
296 self.0
297 }
298
299 pub fn try_read_bytes(bytes: &mut &[u8]) -> Result<Self, &'static str> {
301 if bytes.is_empty() {
302 return Err("Insufficient bytes for version");
303 }
304
305 let version = bytes[0];
306 *bytes = &bytes[1..];
307 Self::new(version)
308 }
309
310 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
312 let mut slice = bytes;
313 Self::try_read_bytes(&mut slice)
314 }
315}
316
317impl Decode for Version {
318 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
319 let version = input.read_byte()?;
320 let version_array = [version];
321
322 Self::try_read_bytes(&mut version_array.as_slice()).map_err(Error::from)
323 }
324}
325
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode)]
328pub struct HeaderLength(u8);
329
330impl HeaderLength {
331 pub fn new(hlen: u8) -> Result<Self, &'static str> {
332 if hlen < MINIMAL_HLEN {
333 Err("Header length is less than minimal Sails header length")
334 } else {
335 Ok(Self(hlen))
336 }
337 }
338
339 pub fn inner(&self) -> u8 {
341 self.0
342 }
343
344 pub fn try_read_bytes(bytes: &mut &[u8]) -> Result<Self, &'static str> {
346 if bytes.is_empty() {
347 return Err("Insufficient bytes for header length");
348 }
349
350 let hlen = bytes[0];
351 *bytes = &bytes[1..];
352 Self::new(hlen)
353 }
354
355 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
357 let mut slice = bytes;
358 Self::try_read_bytes(&mut slice)
359 }
360}
361
362impl Decode for HeaderLength {
363 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
364 let hlen = input.read_byte()?;
365
366 let hlen_array = [hlen];
367 Self::try_read_bytes(&mut hlen_array.as_slice()).map_err(Error::from)
368 }
369}
370
371#[derive(Debug)]
378pub struct MatchedInterface {
379 interface_id: InterfaceId,
380 route_id: u8,
381 entry_id: u16,
382}
383
384impl MatchedInterface {
385 pub fn into_inner(self) -> (InterfaceId, u8, u16) {
387 (self.interface_id, self.route_id, self.entry_id)
388 }
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394 use alloc::vec;
395
396 #[test]
397 fn try_from_bytes_does_not_move_offset() {
398 let magic = Magic::new();
399 let bytes = magic.as_bytes();
400
401 let _ = Magic::try_from_bytes(bytes).expect("same bytes");
402 assert_eq!(bytes, [0x47, 0x4D]);
403 }
404
405 #[test]
406 fn magic_codec() {
407 let magic = Magic::new();
408
409 let encoded = magic.encode();
410 assert_eq!(MAGIC_BYTES.encode(), encoded);
411 let decoded = Magic::decode(&mut &encoded[..]).unwrap();
412
413 assert_eq!(magic, decoded);
414 }
415
416 #[test]
417 fn magic_serde() {
418 let magic = Magic::new();
419
420 let mut serialized = magic.as_bytes();
421 assert_eq!(MAGIC_BYTES, serialized);
422 let deserialized = Magic::try_read_bytes(&mut serialized).unwrap();
423
424 assert_eq!(magic, deserialized);
425 }
426
427 #[test]
428 fn magic_try_read_fails() {
429 let invalid_bytes = [0x00, 0x00];
431 let mut slice = invalid_bytes.as_slice();
432 let result = Magic::try_read_bytes(&mut slice);
433
434 assert!(result.is_err());
435 assert_eq!(result.unwrap_err(), "Invalid Sails magic bytes");
436
437 let short_bytes = [0x47];
439 let mut slice = short_bytes.as_slice();
440 let result = Magic::try_read_bytes(&mut slice);
441
442 assert!(result.is_err());
443 assert_eq!(result.unwrap_err(), "Insufficient bytes for magic");
444 }
445
446 #[test]
447 fn version_serde() {
448 let version = Version::new(1).unwrap();
449
450 let serialized = version.inner();
451 assert_eq!(1u8, serialized);
452 let deserialized = Version::try_read_bytes(&mut [serialized].as_slice()).unwrap();
453
454 assert_eq!(version, deserialized);
455 }
456
457 #[test]
458 fn version_codec() {
459 let version = Version::new(1).unwrap();
460
461 let encoded = version.encode();
462 assert_eq!(1u8.encode(), encoded);
463 let decoded = Version::decode(&mut &encoded[..]).unwrap();
464
465 assert_eq!(version, decoded);
466 }
467
468 #[test]
469 fn version_try_read_fails() {
470 let bytes1 = [255];
472 let bytes2 = [0];
473 let mut slice1 = bytes1.as_slice();
474 let mut slice2 = bytes2.as_slice();
475
476 let result1 = Version::try_read_bytes(&mut slice1);
477 let result2 = Version::try_read_bytes(&mut slice2);
478 assert!(result1.is_err());
479 assert!(result2.is_err());
480
481 assert_eq!(result1.unwrap_err(), "Unsupported Sails version");
482 assert_eq!(result2.unwrap_err(), "Unsupported Sails version");
483
484 let bytes: [u8; 0] = [];
486 let mut slice = bytes.as_slice();
487 let result = Version::try_read_bytes(&mut slice);
488
489 assert!(result.is_err());
490 assert_eq!(result.unwrap_err(), "Insufficient bytes for version");
491 }
492
493 #[test]
494 fn version_latest() {
495 let version = Version::latest();
496 assert_eq!(version.inner(), HIGHEST_SUPPORTED_VERSION);
497 }
498
499 #[test]
500 fn header_length_serde() {
501 let hlen = HeaderLength::new(20).unwrap();
502
503 let serialized = hlen.inner();
504 assert_eq!(20u8, serialized);
505 let deserialized = HeaderLength::try_read_bytes(&mut [serialized].as_slice()).unwrap();
506
507 assert_eq!(hlen, deserialized);
508 }
509
510 #[test]
511 fn header_length_codec() {
512 let hlen = HeaderLength::new(20).unwrap();
513
514 let encoded = hlen.encode();
515 assert_eq!(20u8.encode(), encoded);
516 let decoded = HeaderLength::decode(&mut &encoded[..]).unwrap();
517
518 assert_eq!(hlen, decoded);
519 }
520
521 #[test]
522 fn header_try_read_fails() {
523 let bytes1 = [MINIMAL_HLEN - 1];
525 let mut slice1 = bytes1.as_slice();
526 let result1 = HeaderLength::try_read_bytes(&mut slice1);
527
528 assert!(result1.is_err());
529 assert_eq!(
530 result1.unwrap_err(),
531 "Header length is less than minimal Sails header length"
532 );
533
534 let bytes: [u8; 0] = [];
536 let mut slice = bytes.as_slice();
537 let result2 = HeaderLength::try_read_bytes(&mut slice);
538
539 assert!(result2.is_err());
540 assert_eq!(result2.unwrap_err(), "Insufficient bytes for header length");
541 }
542
543 #[test]
544 fn message_header_serde() {
545 let header = SailsMessageHeader {
546 version: Version::new(1).unwrap(),
547 hlen: HeaderLength::new(MINIMAL_HLEN).unwrap(),
548 interface_id: InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]),
549 route_id: 42,
550 entry_id: 1234,
551 };
552
553 let bytes = header.to_bytes();
554 assert_eq!(bytes.len(), MINIMAL_HLEN as usize);
555 assert_eq!(
556 bytes,
557 vec![
558 0x47, 0x4D, 1, 16, 1, 2, 3, 4, 5, 6, 7, 8, 210, 4, 42, 0, ]
566 );
567
568 let mut slice = bytes.as_slice();
569 let deserialized = SailsMessageHeader::try_read_bytes(&mut slice).unwrap();
570
571 assert_eq!(header, deserialized);
572 assert_eq!(slice.len(), 0); }
574
575 #[test]
576 fn message_header_try_read_fails_invalid_magic() {
577 let bytes = [0x47, 0x4D, 1, 15, 1, 2, 3, 4, 5, 6, 7, 8, 210, 4];
579
580 let mut slice = bytes.as_slice();
581 let result = SailsMessageHeader::try_read_bytes(&mut slice);
582
583 assert!(result.is_err());
584 assert_eq!(result.unwrap_err(), "Insufficient bytes for header");
585 }
586
587 #[test]
588 fn message_header_serde_with_surplus() {
589 let header_bytes = vec![
590 0x47, 0x4D, 1, 16, 1, 2, 3, 4, 5, 6, 7, 8, 210, 4, 42, 0, 99, 100, 101,
599 ];
600 let mut slice = header_bytes.as_slice();
601 let deserialized = SailsMessageHeader::try_read_bytes(&mut slice).unwrap();
602 assert_eq!(deserialized.version, Version::new(1).unwrap());
603 assert_eq!(deserialized.hlen, HeaderLength::new(MINIMAL_HLEN).unwrap());
604 assert_eq!(
605 deserialized.interface_id,
606 InterfaceId([1, 2, 3, 4, 5, 6, 7, 8])
607 );
608 assert_eq!(deserialized.entry_id, 1234);
609 assert_eq!(deserialized.route_id, 42);
610 assert_eq!(slice, &[99, 100, 101]); }
612
613 #[test]
614 fn message_header_with_non_zero_reserved_fails() {
615 let header_bytes = vec![
617 0x47, 0x4D, 1, 16, 1, 2, 3, 4, 5, 6, 7, 8, 210, 4, 42, 1, ];
625 let mut slice = header_bytes.as_slice();
626 let result = SailsMessageHeader::try_read_bytes(&mut slice);
627
628 assert!(result.is_err());
629 assert_eq!(
630 result.unwrap_err(),
631 "Reserved byte must be zero in version 1"
632 );
633
634 let header_bytes = vec![
636 0x47, 0x4D, 2, 16, 1, 2, 3, 4, 5, 6, 7, 8, 210, 4, 42, 1, ];
644 let mut slice = header_bytes.as_slice();
645 let result = SailsMessageHeader::try_read_bytes(&mut slice);
646 assert!(result.is_err());
647 assert_eq!(result.unwrap_err(), "Unsupported Sails version");
648 }
649
650 #[test]
651 fn match_interfaces_works() {
652 let header = SailsMessageHeader {
654 version: Version::new(1).unwrap(),
655 hlen: HeaderLength::new(16).unwrap(),
656 interface_id: InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]),
657 route_id: 1,
658 entry_id: 100,
659 };
660
661 let interfaces = [(InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]), 1)];
662 let result = header.try_match_interfaces(&interfaces).unwrap();
663 let (iid, rid, eid) = result.into_inner();
664
665 assert_eq!(iid, InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]));
666 assert_eq!(rid, 1);
667 assert_eq!(eid, 100);
668
669 let header = SailsMessageHeader {
671 version: Version::new(1).unwrap(),
672 hlen: HeaderLength::new(16).unwrap(),
673 interface_id: InterfaceId([9, 8, 7, 6, 5, 4, 3, 2]),
674 route_id: 0,
675 entry_id: 200,
676 };
677
678 let interfaces = [
679 (InterfaceId([9, 8, 7, 6, 5, 4, 3, 2]), 42),
680 (InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]), 42),
681 ];
682
683 let result = header.try_match_interfaces(&interfaces).unwrap();
684 let (iid, rid, eid) = result.into_inner();
685 assert_eq!(iid, InterfaceId([9, 8, 7, 6, 5, 4, 3, 2]));
686 assert_eq!(rid, 42);
687 assert_eq!(eid, 200);
688 }
689
690 #[test]
691 fn match_interfaces_route_zero_resolves_to_program_route() {
692 let header = SailsMessageHeader {
693 version: Version::new(1).unwrap(),
694 hlen: HeaderLength::new(16).unwrap(),
695 interface_id: InterfaceId([7, 7, 7, 7, 7, 7, 7, 7]),
696 route_id: 0,
697 entry_id: 300,
698 };
699
700 let interfaces = [
701 (InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]), 10),
702 (InterfaceId([7, 7, 7, 7, 7, 7, 7, 7]), 99),
703 ];
704
705 let result = header.try_match_interfaces(&interfaces).unwrap();
706 let (iid, rid, eid) = result.into_inner();
707 assert_eq!(iid, InterfaceId([7, 7, 7, 7, 7, 7, 7, 7]));
708 assert_eq!(rid, 99);
709 assert_eq!(eid, 300);
710 }
711
712 #[test]
713 fn match_interfaces_no_match() {
714 let header = SailsMessageHeader {
715 version: Version::new(1).unwrap(),
716 hlen: HeaderLength::new(16).unwrap(),
717 interface_id: InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]),
718 route_id: 1,
719 entry_id: 100,
720 };
721
722 let interfaces = [(InterfaceId([9, 9, 9, 9, 9, 9, 9, 9]), 1)];
723 let result = header.try_match_interfaces(&interfaces);
724
725 assert!(result.is_err());
726 assert_eq!(
727 result.unwrap_err(),
728 "No matching interface and route ID found"
729 );
730 }
731
732 #[test]
733 fn match_interfaces_multiple_same_interface_with_route_zero() {
734 let header = SailsMessageHeader {
735 version: Version::new(1).unwrap(),
736 hlen: HeaderLength::new(16).unwrap(),
737 interface_id: InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]),
738 route_id: 0,
739 entry_id: 100,
740 };
741
742 let interfaces = [
743 (InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]), 1),
744 (InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]), 2),
745 ];
746 let result = header.try_match_interfaces(&interfaces);
747
748 assert!(result.is_err());
749 assert_eq!(
750 result.unwrap_err(),
751 "Can't infer the interface by route ID `0`, many instances"
752 );
753 }
754
755 #[test]
756 fn match_interfaces_route_mismatch() {
757 let header = SailsMessageHeader {
758 version: Version::new(1).unwrap(),
759 hlen: HeaderLength::new(16).unwrap(),
760 interface_id: InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]),
761 route_id: 5,
762 entry_id: 100,
763 };
764
765 let interfaces = [(InterfaceId([1, 2, 3, 4, 5, 6, 7, 8]), 1)];
766 let result = header.try_match_interfaces(&interfaces);
767
768 assert!(result.is_err());
769 assert_eq!(
770 result.unwrap_err(),
771 "No matching interface and route ID found"
772 );
773 }
774}