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 Session = 2,
21
22 Undefined = 15,
24}
25
26impl ReservedTag {
27 pub const UPPER_BOUND: u64 = Self::Undefined as u64 + 1;
31
32 pub fn range() -> Range<u64> {
34 0..Self::UPPER_BOUND
35 }
36}
37
38impl From<ReservedTag> for Tag {
39 fn from(tag: ReservedTag) -> Self {
40 (tag as u64).into()
41 }
42}
43
44#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
50pub enum Tag {
51 Reserved(u64),
52 Application(u64),
53}
54
55impl Tag {
56 pub const APPLICATION_TAG_RANGE: Range<Self> =
58 (Self::Application(ReservedTag::Undefined as u64 + 1))..Self::Application(Self::MAX);
59 pub const MAX: u64 = 0x1fffffffffffffff_u64;
64 pub const SIZE: usize = size_of::<u64>();
66
67 pub fn from_be_bytes(bytes: [u8; Self::SIZE]) -> Self {
68 let tag = u64::from_be_bytes(bytes);
69 tag.into()
70 }
71
72 pub fn to_be_bytes(&self) -> [u8; Self::SIZE] {
73 match self {
74 Tag::Reserved(tag) | Tag::Application(tag) => tag.to_be_bytes(),
75 }
76 }
77
78 pub fn as_u64(&self) -> u64 {
79 match self {
80 Tag::Reserved(tag) | Tag::Application(tag) => (*tag) & Self::MAX,
81 }
82 }
83}
84
85impl<T: Into<u64>> From<T> for Tag {
86 fn from(tag: T) -> Self {
87 let tag: u64 = tag.into() & Self::MAX;
89
90 if ReservedTag::range().contains(&tag) {
91 Tag::Reserved(
92 ReservedTag::iter()
93 .find(|&t| t as u64 == tag)
94 .unwrap_or(ReservedTag::Undefined) as u64,
95 )
96 } else {
97 Tag::Application(tag)
98 }
99 }
100}
101
102#[cfg(feature = "serde")]
103impl serde::Serialize for Tag {
104 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
105 where
106 S: serde::Serializer,
107 {
108 serializer.serialize_u64(self.as_u64())
109 }
110}
111
112#[cfg(feature = "serde")]
113impl<'a> serde::Deserialize<'a> for Tag {
114 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115 where
116 D: serde::Deserializer<'a>,
117 {
118 Ok(u64::deserialize(deserializer)?.into())
119 }
120}
121
122impl std::fmt::Display for Tag {
123 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
124 write!(f, "{}", self.as_u64())
125 }
126}
127
128impl FromStr for Tag {
129 type Err = std::num::ParseIntError;
130
131 fn from_str(s: &str) -> Result<Self, Self::Err> {
132 u64::from_str(s).map(Tag::from)
133 }
134}
135
136#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
142pub struct IncomingPacketInfo {
143 pub signals_from_sender: PacketSignals,
145 pub num_saved_surbs: usize,
147 pub num_evicted_surbs: usize,
153}
154
155#[derive(Copy, Clone, Debug, PartialEq, Eq)]
161pub struct OutgoingPacketInfo {
162 pub signals_to_destination: PacketSignals,
164 pub max_surbs_in_packet: usize,
166}
167
168impl Default for OutgoingPacketInfo {
169 fn default() -> Self {
170 Self {
171 signals_to_destination: PacketSignals::empty(),
172 max_surbs_in_packet: usize::MAX,
173 }
174 }
175}
176
177#[derive(Clone, Debug, PartialEq, Eq)]
179#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
180pub struct ApplicationDataIn {
181 pub data: ApplicationData,
183 #[cfg_attr(feature = "serde", serde(skip))]
187 pub packet_info: IncomingPacketInfo,
188}
189
190impl ApplicationDataIn {
191 pub fn num_surbs_with_msg(&self) -> usize {
193 self.packet_info
194 .num_saved_surbs
195 .min(HoprPacket::max_surbs_with_message(self.data.total_len()))
196 }
197}
198
199#[derive(Clone, Debug, PartialEq, Eq)]
201#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
202pub struct ApplicationDataOut {
203 pub data: ApplicationData,
205 #[cfg_attr(feature = "serde", serde(skip))]
210 pub packet_info: Option<OutgoingPacketInfo>,
211}
212
213impl ApplicationDataOut {
214 pub fn with_no_packet_info(data: ApplicationData) -> Self {
216 Self {
217 data,
218 packet_info: None,
219 }
220 }
221
222 pub fn estimate_surbs_with_msg(&self) -> usize {
224 let max_possible = HoprPacket::max_surbs_with_message(self.data.total_len());
225 self.packet_info
226 .map(|info| info.max_surbs_in_packet.min(max_possible))
227 .unwrap_or(max_possible)
228 }
229}
230
231#[derive(Clone, PartialEq, Eq)]
236#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
237pub struct ApplicationData {
238 pub application_tag: Tag,
240 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
242 pub plain_text: Box<[u8]>,
243}
244
245impl ApplicationData {
246 pub const PAYLOAD_SIZE: usize = HoprPacket::PAYLOAD_SIZE - Tag::SIZE;
248
249 pub fn new<'a, T: Into<Tag>, D: Into<Cow<'a, [u8]>>>(
253 application_tag: T,
254 plain_text: D,
255 ) -> crate::errors::Result<Self> {
256 let data = plain_text.into();
257 if data.len() <= Self::PAYLOAD_SIZE {
258 Ok(Self {
259 application_tag: application_tag.into(),
260 plain_text: data.into(),
261 })
262 } else {
263 Err(ApplicationLayerError::PayloadTooLarge)
264 }
265 }
266
267 #[inline]
271 pub fn total_len(&self) -> usize {
272 Tag::SIZE + self.plain_text.len()
273 }
274
275 #[inline]
277 pub fn is_payload_empty(&self) -> bool {
278 self.plain_text.is_empty()
279 }
280
281 pub fn to_bytes(&self) -> Box<[u8]> {
283 let mut buf = Vec::with_capacity(Tag::SIZE + self.plain_text.len());
284 buf.extend_from_slice(&self.application_tag.to_be_bytes());
285 buf.extend_from_slice(&self.plain_text);
286 buf.into_boxed_slice()
287 }
288}
289
290impl std::fmt::Debug for ApplicationData {
291 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
292 f.debug_struct("ApplicationData")
293 .field("application_tag", &self.application_tag)
294 .field("plain_text", &to_hex_shortened::<32>(&self.plain_text))
295 .finish()
296 }
297}
298
299impl std::fmt::Display for ApplicationData {
300 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
301 write!(
302 f,
303 "({}): {}",
304 self.application_tag,
305 to_hex_shortened::<16>(&self.plain_text)
306 )
307 }
308}
309
310impl TryFrom<&[u8]> for ApplicationData {
311 type Error = ApplicationLayerError;
312
313 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
314 if value.len() >= Tag::SIZE && value.len() <= HoprPacket::PAYLOAD_SIZE {
315 Ok(Self {
316 application_tag: Tag::from_be_bytes(
317 value[0..Tag::SIZE]
318 .try_into()
319 .map_err(|_e| ApplicationLayerError::DecodingError("ApplicationData.tag".into()))?,
320 ),
321 plain_text: Box::from(&value[Tag::SIZE..]),
322 })
323 } else {
324 Err(ApplicationLayerError::DecodingError("ApplicationData.size".into()))
325 }
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332
333 #[test]
334 fn reserved_tag_v1_range_is_stable() {
335 let range = ReservedTag::range();
336 assert_eq!(range.start, 0);
337 assert_eq!(range.count(), 16); }
339
340 #[test]
341 fn tag_should_be_obtainable_as_reserved_when_created_from_a_reserved_range() {
342 let reserved_tag = ReservedTag::Ping as u64;
343
344 assert_eq!(Tag::from(reserved_tag), Tag::Reserved(reserved_tag));
345 }
346
347 #[test]
348 fn v1_tags_should_have_3_most_significant_bits_unset() {
349 let tag: Tag = u64::MAX.into();
350 assert_eq!(tag.as_u64(), Tag::MAX);
351 }
352
353 #[test]
354 fn tag_should_be_obtainable_as_undefined_reserved_when_created_from_an_undefined_value_in_reserved_range() {
355 let reserved_tag_without_assignment = 7u64;
356
357 assert_eq!(
358 Tag::from(reserved_tag_without_assignment),
359 Tag::Reserved(ReservedTag::Undefined as u64)
360 );
361 }
362
363 #[test]
364 fn v1_format_is_binary_stable() -> anyhow::Result<()> {
365 let original = ApplicationData::new(10u64, &[0_u8, 1_u8])?;
366 let reserialized = ApplicationData::try_from(original.to_bytes().as_ref())?;
367 let reserialized = ApplicationData::try_from(reserialized.to_bytes().as_ref())?;
368
369 assert_eq!(original, reserialized);
370
371 Ok(())
372 }
373
374 #[test]
375 fn test_application_data() -> anyhow::Result<()> {
376 let ad_1 = ApplicationData::new(10u64, &[0_u8, 1_u8])?;
377 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
378 assert_eq!(ad_1, ad_2);
379
380 let ad_1 = ApplicationData::new(0u64, &[])?;
381 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
382 assert_eq!(ad_1, ad_2);
383
384 let ad_1 = ApplicationData::new(10u64, &[0_u8, 1_u8])?;
385 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
386 assert_eq!(ad_1, ad_2);
387
388 let ad_1 = ApplicationData::new(10u64, &[0_u8; ApplicationData::PAYLOAD_SIZE])?;
389 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
390 assert_eq!(ad_1, ad_2);
391
392 assert!(ApplicationData::try_from([0_u8; Tag::SIZE - 1].as_ref()).is_err());
393 assert!(ApplicationData::try_from([0_u8; ApplicationData::PAYLOAD_SIZE + Tag::SIZE + 1].as_ref()).is_err());
394
395 Ok(())
396 }
397
398 #[test]
399 fn application_data_should_not_allow_payload_larger_than_hopr_packet_payload_size() {
400 assert!(ApplicationData::new(10u64, [0_u8; HoprPacket::PAYLOAD_SIZE + 1].as_ref()).is_err());
401 }
402}