hopr_transport_packet/
v1.rs1use std::{fmt::Formatter, ops::Range};
2
3use strum::IntoEnumIterator;
4
5#[repr(u64)]
7#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, strum::EnumIter)]
8pub enum ReservedTag {
9 Ping = 0,
11
12 SessionStart = 1,
14
15 Undefined = 15,
17}
18
19impl ReservedTag {
20 pub fn range() -> Range<u64> {
22 0..(Self::iter().max().unwrap_or(Self::Undefined) as u64 + 1)
23 }
24}
25
26impl From<ReservedTag> for Tag {
27 fn from(tag: ReservedTag) -> Self {
28 (tag as u64).into()
29 }
30}
31
32#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub enum Tag {
38 Reserved(u64),
39 Application(u64),
40}
41
42impl Tag {
43 pub const APPLICATION_TAG_RANGE: Range<Self> =
45 (Self::Application(ReservedTag::Undefined as u64 + 1))..Self::Application(Self::MAX);
46 pub const MAX: u64 = u64::MAX;
47 pub const SIZE: usize = size_of::<u64>();
48
49 pub fn from_be_bytes(bytes: [u8; Self::SIZE]) -> Self {
50 let tag = u64::from_be_bytes(bytes);
51 tag.into()
52 }
53
54 pub fn to_be_bytes(&self) -> [u8; Self::SIZE] {
55 match self {
56 Tag::Reserved(tag) | Tag::Application(tag) => tag.to_be_bytes(),
57 }
58 }
59
60 pub fn as_u64(&self) -> u64 {
61 match self {
62 Tag::Reserved(tag) | Tag::Application(tag) => *tag,
63 }
64 }
65}
66
67impl<T: Into<u64>> From<T> for Tag {
68 fn from(tag: T) -> Self {
69 let tag: u64 = tag.into();
70
71 if ReservedTag::range().contains(&tag) {
72 Tag::Reserved(
73 ReservedTag::iter()
74 .find(|&t| t as u64 == tag)
75 .unwrap_or(ReservedTag::Undefined) as u64,
76 )
77 } else {
78 Tag::Application(tag)
79 }
80 }
81}
82
83#[cfg(feature = "serde")]
84impl serde::Serialize for Tag {
85 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86 where
87 S: serde::Serializer,
88 {
89 match self {
90 Tag::Reserved(tag) | Tag::Application(tag) => serializer.serialize_u64(*tag),
91 }
92 }
93}
94
95#[cfg(feature = "serde")]
96impl<'a> serde::Deserialize<'a> for Tag {
97 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
98 where
99 D: serde::Deserializer<'a>,
100 {
101 let value = u64::deserialize(deserializer)?;
102
103 Ok(value.into())
104 }
105}
106
107impl std::fmt::Display for Tag {
108 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
109 write!(f, "Tag({})", self.as_u64())
110 }
111}
112
113#[derive(Clone, PartialEq, Eq)]
115#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
116pub struct ApplicationData {
117 pub application_tag: Tag,
118 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
119 pub plain_text: Box<[u8]>,
120}
121
122impl ApplicationData {
123 pub const PAYLOAD_SIZE: usize = hopr_crypto_packet::prelude::HoprPacket::PAYLOAD_SIZE - Tag::SIZE;
124
125 pub fn new<T: Into<Tag>>(application_tag: T, plain_text: &[u8]) -> Self {
126 Self {
127 application_tag: application_tag.into(),
128 plain_text: plain_text.into(),
129 }
130 }
131
132 pub fn new_from_owned<T: Into<Tag>>(tag: T, plain_text: Box<[u8]>) -> Self {
133 Self {
134 application_tag: tag.into(),
135 plain_text,
136 }
137 }
138
139 #[allow(clippy::len_without_is_empty)]
140 pub fn len(&self) -> usize {
141 Self::TAG_SIZE + self.plain_text.len()
142 }
143}
144
145impl std::fmt::Debug for ApplicationData {
146 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
147 f.debug_struct("ApplicationData")
148 .field("application_tag", &self.application_tag)
149 .finish()
150 }
151}
152
153impl std::fmt::Display for ApplicationData {
154 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
155 write!(f, "({}): {}", self.application_tag, hex::encode(&self.plain_text))
156 }
157}
158
159impl ApplicationData {
160 const TAG_SIZE: usize = Tag::SIZE;
161
162 pub fn from_bytes(data: &[u8]) -> crate::errors::Result<Self> {
163 if data.len() >= Self::TAG_SIZE {
164 Ok(Self {
165 application_tag: Tag::from_be_bytes(
166 data[0..Self::TAG_SIZE]
167 .try_into()
168 .map_err(|_e| crate::errors::PacketError::DecodingError("ApplicationData.tag".into()))?,
169 ),
170 plain_text: Box::from(&data[Self::TAG_SIZE..]),
171 })
172 } else {
173 Err(crate::errors::PacketError::DecodingError("ApplicationData".into()))
174 }
175 }
176
177 pub fn to_bytes(&self) -> Box<[u8]> {
178 let mut buf = Vec::with_capacity(Self::TAG_SIZE + self.plain_text.len());
179 buf.extend_from_slice(&self.application_tag.to_be_bytes());
180 buf.extend_from_slice(&self.plain_text);
181 buf.into_boxed_slice()
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn reserved_tag_v1_range_is_stable() {
191 let range = ReservedTag::range();
192 assert_eq!(range.start, 0);
193 assert_eq!(range.count(), 16); }
195
196 #[test]
197 fn tag_should_be_obtainable_as_reserved_when_created_from_a_reserved_range() {
198 let reserved_tag = ReservedTag::Ping as u64;
199
200 assert_eq!(Tag::from(reserved_tag), Tag::Reserved(reserved_tag));
201 }
202
203 #[test]
204 fn tag_should_be_obtainable_as_undefined_reserved_when_created_from_an_undefined_value_in_reserved_range() {
205 let reserved_tag_without_assignment = 7u64;
206
207 assert_eq!(
208 Tag::from(reserved_tag_without_assignment),
209 Tag::Reserved(ReservedTag::Undefined as u64)
210 );
211 }
212
213 #[test]
214 fn v1_format_is_binary_stable() -> anyhow::Result<()> {
215 let original = ApplicationData::new(10u64, &[0_u8, 1_u8]);
216 let reserialized = ApplicationData::from_bytes(&original.to_bytes())?;
217 let reserialized = ApplicationData::from_bytes(&reserialized.to_bytes())?;
218
219 assert_eq!(original, reserialized);
220
221 Ok(())
222 }
223
224 #[test]
225 fn test_application_data() -> anyhow::Result<()> {
226 let ad_1 = ApplicationData::new(10u64, &[0_u8, 1_u8]);
227 let ad_2 = ApplicationData::from_bytes(&ad_1.to_bytes())?;
228 assert_eq!(ad_1, ad_2);
229
230 let ad_1 = ApplicationData::new(0u64, &[]);
231 let ad_2 = ApplicationData::from_bytes(&ad_1.to_bytes())?;
232 assert_eq!(ad_1, ad_2);
233
234 let ad_1 = ApplicationData::new(10u64, &[0_u8, 1_u8]);
235 let ad_2 = ApplicationData::from_bytes(&ad_1.to_bytes())?;
236 assert_eq!(ad_1, ad_2);
237
238 Ok(())
239 }
240}