ps_ecc/long/header/methods/
from_bytes.rs1use ps_util::Array;
2
3use crate::{
4 long::{
5 header::{
6 magic::{LONG_ECC_HEADER_MAGIC, LONG_ECC_HEADER_VERSION},
7 utils::calculate_header_checksum,
8 },
9 LongEccHeader, HEADER_SIZE,
10 },
11 LongEccHeaderFromBytesError, ReedSolomon,
12};
13
14impl LongEccHeader {
15 pub fn from_bytes(mut bytes: [u8; 32]) -> Result<Self, LongEccHeaderFromBytesError> {
24 let (body, parity) = bytes.split_at_mut(24);
25
26 ReedSolomon::correct_detached_in_place(parity, body)?;
27
28 let magic = u16::from_be_bytes(*bytes.subarray(0));
29 let version = bytes[2];
30 let parity = bytes[3];
31 let full_length = u32::from_be_bytes(*bytes.subarray(4));
32 let message_length = u32::from_be_bytes(*bytes.subarray(8));
33 let header_checksum = u32::from_be_bytes(*bytes.subarray(12));
34 let checksum = u64::from_be_bytes(*bytes.subarray(16));
35 let header_parity = *bytes.subarray(24);
36
37 if magic != LONG_ECC_HEADER_MAGIC {
38 return Err(LongEccHeaderFromBytesError::IncorrectMagic(magic));
39 }
40
41 if version != LONG_ECC_HEADER_VERSION {
42 return Err(LongEccHeaderFromBytesError::InvalidVersion(version));
43 }
44
45 if header_checksum != {
46 calculate_header_checksum(version, parity, full_length, message_length)
47 } {
48 return Err(LongEccHeaderFromBytesError::IncorrectChecksum);
49 }
50
51 if u64::from(full_length) < u64::from(message_length) + HEADER_SIZE as u64 {
56 return Err(LongEccHeaderFromBytesError::InvalidMessageLength(
57 message_length,
58 full_length,
59 ));
60 }
61
62 let header = Self {
63 magic,
64 version,
65 parity,
66 full_length,
67 message_length,
68 header_checksum,
69 checksum,
70 header_parity,
71 };
72
73 if header.parity_bytes() >= header.segment_distance() {
76 return Err(LongEccHeaderFromBytesError::InvalidSegmentParityRatio(
77 header.segment_distance(),
78 header.parity(),
79 ));
80 }
81
82 let expected_length = HEADER_SIZE as u64
85 + u64::from(message_length)
86 + u64::from(header.parity_bytes()) * u64::from(header.segment_count());
87
88 if u64::from(full_length) != expected_length {
89 return Err(LongEccHeaderFromBytesError::InvalidFullLength(
90 full_length,
91 expected_length,
92 ));
93 }
94
95 Ok(header)
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use crate::long::{
102 header::{
103 magic::{LONG_ECC_HEADER_MAGIC, LONG_ECC_HEADER_VERSION},
104 utils::calculate_header_checksum,
105 RS,
106 },
107 LongEccHeader, OverlapFactor,
108 };
109
110 use super::LongEccHeaderFromBytesError;
111
112 type TestError = Box<dyn std::error::Error>;
113
114 #[test]
115 fn test_from_bytes_rejects_invalid_segment_parity_ratio() -> Result<(), TestError> {
116 let parity = 63 | OverlapFactor::Quadruple.to_u8();
121 let full_length = 1000u32;
122 let message_length = 500u32;
123 let header_checksum =
124 calculate_header_checksum(LONG_ECC_HEADER_VERSION, parity, full_length, message_length);
125
126 let mut bytes = [0u8; 32];
127
128 bytes[..2].copy_from_slice(&LONG_ECC_HEADER_MAGIC.to_be_bytes());
129 bytes[2] = LONG_ECC_HEADER_VERSION;
130 bytes[3] = parity;
131 bytes[4..8].copy_from_slice(&full_length.to_be_bytes());
132 bytes[8..12].copy_from_slice(&message_length.to_be_bytes());
133 bytes[12..16].copy_from_slice(&header_checksum.to_be_bytes());
134
135 let header_parity = RS.generate_parity(&bytes[..24])?;
136
137 bytes[24..].copy_from_slice(&header_parity);
138
139 let result = LongEccHeader::from_bytes(bytes);
140
141 assert_eq!(
142 result,
143 Err(LongEccHeaderFromBytesError::InvalidSegmentParityRatio(
144 32, 63
145 ))
146 );
147
148 Ok(())
149 }
150
151 #[test]
152 fn test_from_bytes_rejects_message_length_exceeding_full_length() -> Result<(), TestError> {
153 let parity = 2 | OverlapFactor::Simple.to_u8();
156 let full_length = 100u32;
157 let message_length = 100u32;
158 let header_checksum =
159 calculate_header_checksum(LONG_ECC_HEADER_VERSION, parity, full_length, message_length);
160
161 let mut bytes = [0u8; 32];
162
163 bytes[..2].copy_from_slice(&LONG_ECC_HEADER_MAGIC.to_be_bytes());
164 bytes[2] = LONG_ECC_HEADER_VERSION;
165 bytes[3] = parity;
166 bytes[4..8].copy_from_slice(&full_length.to_be_bytes());
167 bytes[8..12].copy_from_slice(&message_length.to_be_bytes());
168 bytes[12..16].copy_from_slice(&header_checksum.to_be_bytes());
169
170 let header_parity = RS.generate_parity(&bytes[..24])?;
171
172 bytes[24..].copy_from_slice(&header_parity);
173
174 let result = LongEccHeader::from_bytes(bytes);
175
176 assert_eq!(
177 result,
178 Err(LongEccHeaderFromBytesError::InvalidMessageLength(100, 100))
179 );
180
181 Ok(())
182 }
183
184 #[test]
185 fn test_from_bytes_rejects_incorrect_full_length() -> Result<(), TestError> {
186 let parity = 1 | OverlapFactor::Simple.to_u8();
190 let full_length = 100u32;
191 let message_length = 10u32;
192 let header_checksum =
193 calculate_header_checksum(LONG_ECC_HEADER_VERSION, parity, full_length, message_length);
194
195 let mut bytes = [0u8; 32];
196
197 bytes[..2].copy_from_slice(&LONG_ECC_HEADER_MAGIC.to_be_bytes());
198 bytes[2] = LONG_ECC_HEADER_VERSION;
199 bytes[3] = parity;
200 bytes[4..8].copy_from_slice(&full_length.to_be_bytes());
201 bytes[8..12].copy_from_slice(&message_length.to_be_bytes());
202 bytes[12..16].copy_from_slice(&header_checksum.to_be_bytes());
203
204 let header_parity = RS.generate_parity(&bytes[..24])?;
205
206 bytes[24..].copy_from_slice(&header_parity);
207
208 let result = LongEccHeader::from_bytes(bytes);
209
210 assert_eq!(
211 result,
212 Err(LongEccHeaderFromBytesError::InvalidFullLength(100, 44))
213 );
214
215 Ok(())
216 }
217
218 #[test]
219 fn test_from_bytes_rejects_zero_checksum_for_zero_parity() -> Result<(), TestError> {
220 let parity = OverlapFactor::Simple.to_u8();
223 let full_length = 100u32;
224 let message_length = 50u32;
225
226 let mut bytes = [0u8; 32];
227
228 bytes[..2].copy_from_slice(&LONG_ECC_HEADER_MAGIC.to_be_bytes());
229 bytes[2] = LONG_ECC_HEADER_VERSION;
230 bytes[3] = parity;
231 bytes[4..8].copy_from_slice(&full_length.to_be_bytes());
232 bytes[8..12].copy_from_slice(&message_length.to_be_bytes());
233
234 let header_parity = RS.generate_parity(&bytes[..24])?;
235
236 bytes[24..].copy_from_slice(&header_parity);
237
238 let result = LongEccHeader::from_bytes(bytes);
239
240 assert_eq!(result, Err(LongEccHeaderFromBytesError::IncorrectChecksum));
241
242 Ok(())
243 }
244}