1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
use crate::{
long::{
header::{
magic::LONG_ECC_HEADER_VERSION, utils::calculate_header_checksum, LONG_ECC_HEADER_MAGIC,
},
LongEccHeader, OverlapFactor, HEADER_SIZE,
},
LongEccHeaderConstructorError, MAX_PARITY,
};
use super::super::RS;
impl LongEccHeader {
/// Constructs a header, computing its checksum and parity.
///
/// # Errors
/// Returns `InvalidParityCount` if `parity` exceeds [`MAX_PARITY`],
/// `InvalidMessageLength` if the header and message do not fit within
/// `full_length`, `InvalidSegmentParityRatio` if the parity bytes leave
/// no room for new data within a segment, `InvalidFullLength` if
/// `full_length` does not match the codeword length derived from the
/// message length and segment geometry, or `GenerateParity` if
/// generating the header parity fails.
pub fn new(
parity: u8,
overlap_factor: OverlapFactor,
full_length: u32,
message_length: u32,
checksum: u64,
) -> Result<Self, LongEccHeaderConstructorError> {
let magic = LONG_ECC_HEADER_MAGIC;
let version = LONG_ECC_HEADER_VERSION;
if parity > MAX_PARITY {
return Err(LongEccHeaderConstructorError::InvalidParityCount(parity));
}
// The codeword must hold the header and the message; parity only adds
// to it. The exact-length check below subsumes this bound, but this
// check runs first and is kept for its more specific
// `InvalidMessageLength` diagnostic.
if u64::from(full_length) < u64::from(message_length) + HEADER_SIZE as u64 {
return Err(LongEccHeaderConstructorError::InvalidMessageLength(
message_length,
full_length,
));
}
// Pack the overlap factor into the high two bits of the parity byte.
let parity = parity | overlap_factor.to_u8();
let mut header = Self {
magic,
version,
parity,
full_length,
message_length,
header_checksum: calculate_header_checksum(
version,
parity,
full_length,
message_length,
),
checksum,
header_parity: [0; 8],
};
// Reject headers whose parity leaves no room for new data within a segment;
// the derived segment methods assume `2 * parity < segment_distance`.
if header.parity_bytes() >= header.segment_distance() {
return Err(LongEccHeaderConstructorError::InvalidSegmentParityRatio(
header.segment_distance(),
header.parity(),
));
}
// Defensively require the full length to match the codeword geometry
// exactly: the header, the message, and the parity of every segment.
let expected_length = HEADER_SIZE as u64
+ u64::from(message_length)
+ u64::from(header.parity_bytes()) * u64::from(header.segment_count());
if u64::from(full_length) != expected_length {
return Err(LongEccHeaderConstructorError::InvalidFullLength(
full_length,
expected_length,
));
}
let parity = RS.generate_parity(&header.to_bytes()[..24])?;
header.header_parity.copy_from_slice(&parity);
Ok(header)
}
}
#[cfg(test)]
mod tests {
use crate::{
long::{LongEccHeader, OverlapFactor},
MAX_PARITY,
};
use super::LongEccHeaderConstructorError;
#[test]
fn test_new_rejects_invalid_segment_parity_ratio() {
// With parity 63 and quadruple overlap, each segment holds 126 parity bytes,
// but consecutive segments start only (255 - 126) / 4 = 32 bytes apart.
let result = LongEccHeader::new(63, OverlapFactor::Quadruple, 100, 50, 0);
assert_eq!(
result,
Err(LongEccHeaderConstructorError::InvalidSegmentParityRatio(
32, 63
))
);
}
#[test]
fn test_new_quadruple_overlap_boundary() {
// With parity 25, the 50 parity bytes fit below the segment distance
// of (255 - 50) / 4 = 51; with parity 26, the 52 parity bytes exceed
// the segment distance of (255 - 52) / 4 = 50.
assert!(LongEccHeader::new(25, OverlapFactor::Quadruple, 132, 50, 0).is_ok());
assert_eq!(
LongEccHeader::new(26, OverlapFactor::Quadruple, 132, 50, 0),
Err(LongEccHeaderConstructorError::InvalidSegmentParityRatio(
50, 26
))
);
}
#[test]
fn test_new_double_overlap_boundary() {
// With parity 42, the 84 parity bytes fit below the segment distance
// of (255 - 84) / 2 = 85; with parity 43, the 86 parity bytes exceed
// the segment distance of (255 - 86) / 2 = 84.
assert!(LongEccHeader::new(42, OverlapFactor::Double, 166, 50, 0).is_ok());
assert_eq!(
LongEccHeader::new(43, OverlapFactor::Double, 166, 50, 0),
Err(LongEccHeaderConstructorError::InvalidSegmentParityRatio(
84, 43
))
);
}
#[test]
fn test_new_triple_overlap_boundary() {
// With parity 31, the 62 parity bytes fit below the segment distance
// of (255 - 62) / 3 = 64; with parity 32, the 64 parity bytes exceed
// the segment distance of (255 - 64) / 3 = 63.
assert!(LongEccHeader::new(31, OverlapFactor::Triple, 144, 50, 0).is_ok());
assert_eq!(
LongEccHeader::new(32, OverlapFactor::Triple, 144, 50, 0),
Err(LongEccHeaderConstructorError::InvalidSegmentParityRatio(
63, 32
))
);
}
#[test]
fn test_new_rejects_message_length_exceeding_full_length() {
// Full length 41 is one byte short of the 32-byte header plus 10 message bytes.
let result = LongEccHeader::new(1, OverlapFactor::Simple, 41, 10, 0);
assert_eq!(
result,
Err(LongEccHeaderConstructorError::InvalidMessageLength(10, 41))
);
}
#[test]
fn test_new_rejects_incorrect_full_length() {
// With parity 1, the codeword spans the 32-byte header, 10 message
// bytes, and 2 parity bytes, so the full length must be exactly 44.
assert!(LongEccHeader::new(1, OverlapFactor::Simple, 44, 10, 0).is_ok());
assert_eq!(
LongEccHeader::new(1, OverlapFactor::Simple, 45, 10, 0),
Err(LongEccHeaderConstructorError::InvalidFullLength(45, 44))
);
}
#[test]
fn test_new_accepts_max_parity_without_overlap() {
// With maximum parity and no overlap, the 126 parity bytes fit below
// the segment distance of 255 - 126 = 129.
assert!(LongEccHeader::new(MAX_PARITY, OverlapFactor::Simple, 208, 50, 0).is_ok());
}
}