1use std::{borrow::Cow, fmt::Formatter, ops::Range, str::FromStr};
2
3use hopr_crypto_packet::prelude::{HoprPacket, PacketSignals};
4use hopr_primitive_types::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 fn range() -> Range<u64> {
26 0..(Self::iter().max().unwrap_or(Self::Undefined) as u64 + 1)
27 }
28}
29
30impl From<ReservedTag> for Tag {
31 fn from(tag: ReservedTag) -> Self {
32 (tag as u64).into()
33 }
34}
35
36#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
42pub enum Tag {
43 Reserved(u64),
44 Application(u64),
45}
46
47impl Tag {
48 pub const APPLICATION_TAG_RANGE: Range<Self> =
50 (Self::Application(ReservedTag::Undefined as u64 + 1))..Self::Application(Self::MAX);
51 pub const MAX: u64 = 0x1fffffffffffffff_u64;
56 pub const SIZE: usize = size_of::<u64>();
58
59 pub fn from_be_bytes(bytes: [u8; Self::SIZE]) -> Self {
60 let tag = u64::from_be_bytes(bytes);
61 tag.into()
62 }
63
64 pub fn to_be_bytes(&self) -> [u8; Self::SIZE] {
65 match self {
66 Tag::Reserved(tag) | Tag::Application(tag) => tag.to_be_bytes(),
67 }
68 }
69
70 pub fn as_u64(&self) -> u64 {
71 match self {
72 Tag::Reserved(tag) | Tag::Application(tag) => (*tag) & Self::MAX,
73 }
74 }
75}
76
77impl<T: Into<u64>> From<T> for Tag {
78 fn from(tag: T) -> Self {
79 let tag: u64 = tag.into() & Self::MAX;
81
82 if ReservedTag::range().contains(&tag) {
83 Tag::Reserved(
84 ReservedTag::iter()
85 .find(|&t| t as u64 == tag)
86 .unwrap_or(ReservedTag::Undefined) as u64,
87 )
88 } else {
89 Tag::Application(tag)
90 }
91 }
92}
93
94#[cfg(feature = "serde")]
95impl serde::Serialize for Tag {
96 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
97 where
98 S: serde::Serializer,
99 {
100 serializer.serialize_u64(self.as_u64())
101 }
102}
103
104#[cfg(feature = "serde")]
105impl<'a> serde::Deserialize<'a> for Tag {
106 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
107 where
108 D: serde::Deserializer<'a>,
109 {
110 Ok(u64::deserialize(deserializer)?.into())
111 }
112}
113
114impl std::fmt::Display for Tag {
115 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
116 write!(f, "{}", self.as_u64())
117 }
118}
119
120impl FromStr for Tag {
121 type Err = std::num::ParseIntError;
122
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
124 u64::from_str(s).map(Tag::from)
125 }
126}
127
128#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
134pub struct IncomingPacketInfo {
135 pub signals_from_sender: PacketSignals,
137 pub num_saved_surbs: usize,
139}
140
141#[derive(Copy, Clone, Debug, PartialEq, Eq)]
147pub struct OutgoingPacketInfo {
148 pub signals_to_destination: PacketSignals,
150 pub max_surbs_in_packet: usize,
152}
153
154impl Default for OutgoingPacketInfo {
155 fn default() -> Self {
156 Self {
157 signals_to_destination: PacketSignals::empty(),
158 max_surbs_in_packet: usize::MAX,
159 }
160 }
161}
162
163#[derive(Clone, Debug, PartialEq, Eq)]
165#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
166pub struct ApplicationDataIn {
167 pub data: ApplicationData,
169 #[cfg_attr(feature = "serde", serde(skip))]
173 pub packet_info: IncomingPacketInfo,
174}
175
176impl ApplicationDataIn {
177 pub fn num_surbs_with_msg(&self) -> usize {
179 self.packet_info
180 .num_saved_surbs
181 .min(HoprPacket::max_surbs_with_message(self.data.total_len()))
182 }
183}
184
185#[derive(Clone, Debug, PartialEq, Eq)]
187#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
188pub struct ApplicationDataOut {
189 pub data: ApplicationData,
191 #[cfg_attr(feature = "serde", serde(skip))]
196 pub packet_info: Option<OutgoingPacketInfo>,
197}
198
199impl ApplicationDataOut {
200 pub fn with_no_packet_info(data: ApplicationData) -> Self {
202 Self {
203 data,
204 packet_info: None,
205 }
206 }
207
208 pub fn estimate_surbs_with_msg(&self) -> usize {
210 let max_possible = HoprPacket::max_surbs_with_message(self.data.total_len());
211 self.packet_info
212 .map(|info| info.max_surbs_in_packet.min(max_possible))
213 .unwrap_or(max_possible)
214 }
215}
216
217#[derive(Clone, PartialEq, Eq)]
222#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
223pub struct ApplicationData {
224 pub application_tag: Tag,
226 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
228 pub plain_text: Box<[u8]>,
229}
230
231impl ApplicationData {
232 pub const PAYLOAD_SIZE: usize = HoprPacket::PAYLOAD_SIZE - Tag::SIZE;
234
235 pub fn new<'a, T: Into<Tag>, D: Into<Cow<'a, [u8]>>>(
239 application_tag: T,
240 plain_text: D,
241 ) -> crate::errors::Result<Self> {
242 let data = plain_text.into();
243 if data.len() <= Self::PAYLOAD_SIZE {
244 Ok(Self {
245 application_tag: application_tag.into(),
246 plain_text: data.into(),
247 })
248 } else {
249 Err(ApplicationLayerError::PayloadTooLarge)
250 }
251 }
252
253 #[inline]
257 pub fn total_len(&self) -> usize {
258 Tag::SIZE + self.plain_text.len()
259 }
260
261 #[inline]
263 pub fn is_payload_empty(&self) -> bool {
264 self.plain_text.is_empty()
265 }
266
267 pub fn to_bytes(&self) -> Box<[u8]> {
269 let mut buf = Vec::with_capacity(Tag::SIZE + self.plain_text.len());
270 buf.extend_from_slice(&self.application_tag.to_be_bytes());
271 buf.extend_from_slice(&self.plain_text);
272 buf.into_boxed_slice()
273 }
274}
275
276impl std::fmt::Debug for ApplicationData {
277 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
278 f.debug_struct("ApplicationData")
279 .field("application_tag", &self.application_tag)
280 .field("plain_text", &to_hex_shortened::<32>(&self.plain_text))
281 .finish()
282 }
283}
284
285impl std::fmt::Display for ApplicationData {
286 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
287 write!(
288 f,
289 "({}): {}",
290 self.application_tag,
291 to_hex_shortened::<16>(&self.plain_text)
292 )
293 }
294}
295
296impl TryFrom<&[u8]> for ApplicationData {
297 type Error = ApplicationLayerError;
298
299 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
300 if value.len() >= Tag::SIZE && value.len() <= HoprPacket::PAYLOAD_SIZE {
301 Ok(Self {
302 application_tag: Tag::from_be_bytes(
303 value[0..Tag::SIZE]
304 .try_into()
305 .map_err(|_e| ApplicationLayerError::DecodingError("ApplicationData.tag".into()))?,
306 ),
307 plain_text: Box::from(&value[Tag::SIZE..]),
308 })
309 } else {
310 Err(ApplicationLayerError::DecodingError("ApplicationData.size".into()))
311 }
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn reserved_tag_v1_range_is_stable() {
321 let range = ReservedTag::range();
322 assert_eq!(range.start, 0);
323 assert_eq!(range.count(), 16); }
325
326 #[test]
327 fn tag_should_be_obtainable_as_reserved_when_created_from_a_reserved_range() {
328 let reserved_tag = ReservedTag::Ping as u64;
329
330 assert_eq!(Tag::from(reserved_tag), Tag::Reserved(reserved_tag));
331 }
332
333 #[test]
334 fn v1_tags_should_have_3_most_significant_bits_unset() {
335 let tag: Tag = u64::MAX.into();
336 assert_eq!(tag.as_u64(), Tag::MAX);
337 }
338
339 #[test]
340 fn tag_should_be_obtainable_as_undefined_reserved_when_created_from_an_undefined_value_in_reserved_range() {
341 let reserved_tag_without_assignment = 7u64;
342
343 assert_eq!(
344 Tag::from(reserved_tag_without_assignment),
345 Tag::Reserved(ReservedTag::Undefined as u64)
346 );
347 }
348
349 #[test]
350 fn v1_format_is_binary_stable() -> anyhow::Result<()> {
351 let original = ApplicationData::new(10u64, &[0_u8, 1_u8])?;
352 let reserialized = ApplicationData::try_from(original.to_bytes().as_ref())?;
353 let reserialized = ApplicationData::try_from(reserialized.to_bytes().as_ref())?;
354
355 assert_eq!(original, reserialized);
356
357 Ok(())
358 }
359
360 #[test]
361 fn test_application_data() -> anyhow::Result<()> {
362 let ad_1 = ApplicationData::new(10u64, &[0_u8, 1_u8])?;
363 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
364 assert_eq!(ad_1, ad_2);
365
366 let ad_1 = ApplicationData::new(0u64, &[])?;
367 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
368 assert_eq!(ad_1, ad_2);
369
370 let ad_1 = ApplicationData::new(10u64, &[0_u8, 1_u8])?;
371 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
372 assert_eq!(ad_1, ad_2);
373
374 let ad_1 = ApplicationData::new(10u64, &[0_u8; ApplicationData::PAYLOAD_SIZE])?;
375 let ad_2 = ApplicationData::try_from(ad_1.to_bytes().as_ref())?;
376 assert_eq!(ad_1, ad_2);
377
378 assert!(ApplicationData::try_from([0_u8; Tag::SIZE - 1].as_ref()).is_err());
379 assert!(ApplicationData::try_from([0_u8; ApplicationData::PAYLOAD_SIZE + Tag::SIZE + 1].as_ref()).is_err());
380
381 Ok(())
382 }
383
384 #[test]
385 fn application_data_should_not_allow_payload_larger_than_hopr_packet_payload_size() {
386 assert!(ApplicationData::new(10u64, [0_u8; HoprPacket::PAYLOAD_SIZE + 1].as_ref()).is_err());
387 }
388}