1use std::{borrow::Cow, fmt::Formatter, ops::Range, str::FromStr};
2
3use hopr_crypto_packet::prelude::{HoprPacket, PacketSignals};
4use hopr_types::primitive::to_hex_shortened;
5use strum::IntoEnumIterator;
6
7use crate::errors::ApplicationLayerError;
8
9#[repr(u64)]
11#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, strum::EnumIter)]
12pub enum ReservedTag {
13 Ping = 0,
15
16 SessionStart = 1,
18
19 Undefined = 15,
21}
22
23impl ReservedTag {
24 pub const UPPER_BOUND: u64 = Self::Undefined as u64 + 1;
28
29 pub fn range() -> Range<u64> {
31 0..Self::UPPER_BOUND
32 }
33}
34
35impl From<ReservedTag> for Tag {
36 fn from(tag: ReservedTag) -> Self {
37 (tag as u64).into()
38 }
39}
40
41#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
47pub enum Tag {
48 Reserved(u64),
49 Application(u64),
50}
51
52impl Tag {
53 pub const APPLICATION_TAG_RANGE: Range<Self> =
55 (Self::Application(ReservedTag::Undefined as u64 + 1))..Self::Application(Self::MAX);
56 pub const MAX: u64 = 0x1fffffffffffffff_u64;
61 pub const SIZE: usize = size_of::<u64>();
63
64 pub fn from_be_bytes(bytes: [u8; Self::SIZE]) -> Self {
65 let tag = u64::from_be_bytes(bytes);
66 tag.into()
67 }
68
69 pub fn to_be_bytes(&self) -> [u8; Self::SIZE] {
70 match self {
71 Tag::Reserved(tag) | Tag::Application(tag) => tag.to_be_bytes(),
72 }
73 }
74
75 pub fn as_u64(&self) -> u64 {
76 match self {
77 Tag::Reserved(tag) | Tag::Application(tag) => (*tag) & Self::MAX,
78 }
79 }
80}
81
82impl<T: Into<u64>> From<T> for Tag {
83 fn from(tag: T) -> Self {
84 let tag: u64 = tag.into() & Self::MAX;
86
87 if ReservedTag::range().contains(&tag) {
88 Tag::Reserved(
89 ReservedTag::iter()
90 .find(|&t| t as u64 == tag)
91 .unwrap_or(ReservedTag::Undefined) as u64,
92 )
93 } else {
94 Tag::Application(tag)
95 }
96 }
97}
98
99#[cfg(feature = "serde")]
100impl serde::Serialize for Tag {
101 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102 where
103 S: serde::Serializer,
104 {
105 serializer.serialize_u64(self.as_u64())
106 }
107}
108
109#[cfg(feature = "serde")]
110impl<'a> serde::Deserialize<'a> for Tag {
111 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
112 where
113 D: serde::Deserializer<'a>,
114 {
115 Ok(u64::deserialize(deserializer)?.into())
116 }
117}
118
119impl std::fmt::Display for Tag {
120 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
121 write!(f, "{}", self.as_u64())
122 }
123}
124
125impl FromStr for Tag {
126 type Err = std::num::ParseIntError;
127
128 fn from_str(s: &str) -> Result<Self, Self::Err> {
129 u64::from_str(s).map(Tag::from)
130 }
131}
132
133#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
139pub struct IncomingPacketInfo {
140 pub signals_from_sender: PacketSignals,
142 pub num_saved_surbs: usize,
144}
145
146#[derive(Copy, Clone, Debug, PartialEq, Eq)]
152pub struct OutgoingPacketInfo {
153 pub signals_to_destination: PacketSignals,
155 pub max_surbs_in_packet: usize,
157}
158
159impl Default for OutgoingPacketInfo {
160 fn default() -> Self {
161 Self {
162 signals_to_destination: PacketSignals::empty(),
163 max_surbs_in_packet: usize::MAX,
164 }
165 }
166}
167
168#[derive(Clone, Debug, PartialEq, Eq)]
170#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
171pub struct ApplicationDataIn {
172 pub data: ApplicationData,
174 #[cfg_attr(feature = "serde", serde(skip))]
178 pub packet_info: IncomingPacketInfo,
179}
180
181impl ApplicationDataIn {
182 pub fn num_surbs_with_msg(&self) -> usize {
184 self.packet_info
185 .num_saved_surbs
186 .min(HoprPacket::max_surbs_with_message(self.data.total_len()))
187 }
188}
189
190#[derive(Clone, Debug, PartialEq, Eq)]
192#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
193pub struct ApplicationDataOut {
194 pub data: ApplicationData,
196 #[cfg_attr(feature = "serde", serde(skip))]
201 pub packet_info: Option<OutgoingPacketInfo>,
202}
203
204impl ApplicationDataOut {
205 pub fn with_no_packet_info(data: ApplicationData) -> Self {
207 Self {
208 data,
209 packet_info: None,
210 }
211 }
212
213 pub fn estimate_surbs_with_msg(&self) -> usize {
215 let max_possible = HoprPacket::max_surbs_with_message(self.data.total_len());
216 self.packet_info
217 .map(|info| info.max_surbs_in_packet.min(max_possible))
218 .unwrap_or(max_possible)
219 }
220}
221
222#[derive(Clone, PartialEq, Eq)]
227#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
228pub struct ApplicationData {
229 pub application_tag: Tag,
231 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
233 pub plain_text: Box<[u8]>,
234}
235
236impl ApplicationData {
237 pub const PAYLOAD_SIZE: usize = HoprPacket::PAYLOAD_SIZE - Tag::SIZE;
239
240 pub fn new<'a, T: Into<Tag>, D: Into<Cow<'a, [u8]>>>(
244 application_tag: T,
245 plain_text: D,
246 ) -> crate::errors::Result<Self> {
247 let data = plain_text.into();
248 if data.len() <= Self::PAYLOAD_SIZE {
249 Ok(Self {
250 application_tag: application_tag.into(),
251 plain_text: data.into(),
252 })
253 } else {
254 Err(ApplicationLayerError::PayloadTooLarge)
255 }
256 }
257
258 #[inline]
262 pub fn total_len(&self) -> usize {
263 Tag::SIZE + self.plain_text.len()
264 }
265
266 #[inline]
268 pub fn is_payload_empty(&self) -> bool {
269 self.plain_text.is_empty()
270 }
271
272 pub fn to_bytes(&self) -> Box<[u8]> {
274 let mut buf = Vec::with_capacity(Tag::SIZE + self.plain_text.len());
275 buf.extend_from_slice(&self.application_tag.to_be_bytes());
276 buf.extend_from_slice(&self.plain_text);
277 buf.into_boxed_slice()
278 }
279}
280
281impl std::fmt::Debug for ApplicationData {
282 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
283 f.debug_struct("ApplicationData")
284 .field("application_tag", &self.application_tag)
285 .field("plain_text", &to_hex_shortened::<32>(&self.plain_text))
286 .finish()
287 }
288}
289
290impl std::fmt::Display for ApplicationData {
291 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
292 write!(
293 f,
294 "({}): {}",
295 self.application_tag,
296 to_hex_shortened::<16>(&self.plain_text)
297 )
298 }
299}
300
301impl TryFrom<&[u8]> for ApplicationData {
302 type Error = ApplicationLayerError;
303
304 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
305 if value.len() >= Tag::SIZE && value.len() <= HoprPacket::PAYLOAD_SIZE {
306 Ok(Self {
307 application_tag: Tag::from_be_bytes(
308 value[0..Tag::SIZE]
309 .try_into()
310 .map_err(|_e| ApplicationLayerError::DecodingError("ApplicationData.tag".into()))?,
311 ),
312 plain_text: Box::from(&value[Tag::SIZE..]),
313 })
314 } else {
315 Err(ApplicationLayerError::DecodingError("ApplicationData.size".into()))
316 }
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 #[test]
325 fn reserved_tag_v1_range_is_stable() {
326 let range = ReservedTag::range();
327 assert_eq!(range.start, 0);
328 assert_eq!(range.count(), 16); }
330
331 #[test]
332 fn tag_should_be_obtainable_as_reserved_when_created_from_a_reserved_range() {
333 let reserved_tag = ReservedTag::Ping as u64;
334
335 assert_eq!(Tag::from(reserved_tag), Tag::Reserved(reserved_tag));
336 }
337
338 #[test]
339 fn v1_tags_should_have_3_most_significant_bits_unset() {
340 let tag: Tag = u64::MAX.into();
341 assert_eq!(tag.as_u64(), Tag::MAX);
342 }
343
344 #[test]
345 fn tag_should_be_obtainable_as_undefined_reserved_when_created_from_an_undefined_value_in_reserved_range() {
346 let reserved_tag_without_assignment = 7u64;
347
348 assert_eq!(
349 Tag::from(reserved_tag_without_assignment),
350 Tag::Reserved(ReservedTag::Undefined as u64)
351 );
352 }
353
354 #[test]
355 fn v1_format_is_binary_stable() -> anyhow::Result<()> {
356 let original = ApplicationData::new(10u64, &[0_u8, 1_u8])?;
357 let reserialized = ApplicationData::try_from(original.to_bytes().as_ref())?;
358 let reserialized = ApplicationData::try_from(reserialized.to_bytes().as_ref())?;
359
360 assert_eq!(original, reserialized);
361
362 Ok(())
363 }
364
365 #[test]
366 fn test_application_data() -> anyhow::Result<()> {
367 let ad_1 = ApplicationData::new(10u64, &[0_u8, 1_u8])?;
368 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
369 assert_eq!(ad_1, ad_2);
370
371 let ad_1 = ApplicationData::new(0u64, &[])?;
372 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
373 assert_eq!(ad_1, ad_2);
374
375 let ad_1 = ApplicationData::new(10u64, &[0_u8, 1_u8])?;
376 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
377 assert_eq!(ad_1, ad_2);
378
379 let ad_1 = ApplicationData::new(10u64, &[0_u8; ApplicationData::PAYLOAD_SIZE])?;
380 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
381 assert_eq!(ad_1, ad_2);
382
383 assert!(ApplicationData::try_from([0_u8; Tag::SIZE - 1].as_ref()).is_err());
384 assert!(ApplicationData::try_from([0_u8; ApplicationData::PAYLOAD_SIZE + Tag::SIZE + 1].as_ref()).is_err());
385
386 Ok(())
387 }
388
389 #[test]
390 fn application_data_should_not_allow_payload_larger_than_hopr_packet_payload_size() {
391 assert!(ApplicationData::new(10u64, [0_u8; HoprPacket::PAYLOAD_SIZE + 1].as_ref()).is_err());
392 }
393}