1use std::hash::{Hash, Hasher};
4
5#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
11pub struct LoraBinary {
12 segments: Vec<Vec<u8>>,
13 len: usize,
14}
15
16impl LoraBinary {
17 pub fn from_bytes(bytes: Vec<u8>) -> Self {
18 let len = bytes.len();
19 Self {
20 segments: if bytes.is_empty() {
21 Vec::new()
22 } else {
23 vec![bytes]
24 },
25 len,
26 }
27 }
28
29 pub fn from_segments(segments: Vec<Vec<u8>>) -> Self {
30 let len = segments.iter().map(Vec::len).sum();
31 Self { segments, len }
32 }
33
34 pub fn len(&self) -> usize {
35 self.len
36 }
37
38 pub fn is_empty(&self) -> bool {
39 self.len == 0
40 }
41
42 pub fn segments(&self) -> &[Vec<u8>] {
43 &self.segments
44 }
45
46 pub fn chunks(&self) -> impl Iterator<Item = &[u8]> + '_ {
47 self.segments.iter().map(Vec::as_slice)
48 }
49
50 pub fn into_segments(self) -> Vec<Vec<u8>> {
51 self.segments
52 }
53
54 pub fn to_vec(&self) -> Vec<u8> {
55 let mut out = Vec::with_capacity(self.len);
56 for segment in self.chunks() {
57 out.extend_from_slice(segment);
58 }
59 out
60 }
61}
62
63impl PartialEq for LoraBinary {
64 fn eq(&self, other: &Self) -> bool {
65 self.len == other.len
66 && self
67 .chunks()
68 .flat_map(|segment| segment.iter())
69 .eq(other.chunks().flat_map(|segment| segment.iter()))
70 }
71}
72
73impl Eq for LoraBinary {}
74
75impl Hash for LoraBinary {
76 fn hash<H: Hasher>(&self, state: &mut H) {
77 self.len.hash(state);
78 for segment in self.chunks() {
79 state.write(segment);
80 }
81 }
82}
83
84impl From<Vec<u8>> for LoraBinary {
85 fn from(value: Vec<u8>) -> Self {
86 Self::from_bytes(value)
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use std::collections::hash_map::DefaultHasher;
93 use std::hash::{Hash, Hasher};
94
95 use super::*;
96
97 #[test]
98 fn equality_and_hash_use_logical_bytes_not_chunking() {
99 let contiguous = LoraBinary::from_bytes(vec![1, 2, 3, 4]);
100 let segmented = LoraBinary::from_segments(vec![vec![1, 2], vec![3], vec![4]]);
101
102 assert_eq!(contiguous, segmented);
103 assert_eq!(hash(&contiguous), hash(&segmented));
104 }
105
106 fn hash(value: &LoraBinary) -> u64 {
107 let mut hasher = DefaultHasher::new();
108 value.hash(&mut hasher);
109 hasher.finish()
110 }
111}