commonware_cryptography/reed_solomon/rate/
decoder_work.rs1use crate::reed_solomon::{
2 Error, Plan,
3 engine::{SHARD_CHUNK_BYTES, Shards, ShardsRefMut},
4};
5use fixedbitset::FixedBitSet;
6
7pub struct DecoderWork {
11 original_count: usize,
12 recovery_count: usize,
13 shard_bytes: usize,
14
15 original_base_pos: usize,
17
18 recovery_base_pos: usize,
20
21 original_received_count: usize,
22 recovery_received_count: usize,
23
24 received: FixedBitSet,
28
29 shards: Shards,
31}
32
33impl DecoderWork {
34 pub const fn new() -> Self {
36 Self {
37 original_count: 0,
38 recovery_count: 0,
39 shard_bytes: 0,
40
41 original_base_pos: 0,
42 recovery_base_pos: 0,
43
44 original_received_count: 0,
45 recovery_received_count: 0,
46 received: FixedBitSet::new(),
47 shards: Shards::new(),
48 }
49 }
50}
51
52impl Default for DecoderWork {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl DecoderWork {
59 pub(crate) fn validate_plan(&self, plan: &Plan, high_rate: bool) -> Result<(), Error> {
62 if plan.matches(
63 self.original_count,
64 self.recovery_count,
65 high_rate,
66 &self.received,
67 ) {
68 Ok(())
69 } else {
70 Err(Error::PlanMismatch)
71 }
72 }
73
74 pub(crate) fn add_original_shard<T: AsRef<[u8]>>(
79 &mut self,
80 index: usize,
81 original_shard: T,
82 ) -> Result<(), Error> {
83 if index >= self.original_count {
84 return Err(Error::InvalidOriginalShardIndex {
85 original_count: self.original_count,
86 index,
87 });
88 }
89
90 let pos = self.original_base_pos + index;
91 let original_shard = original_shard.as_ref();
92
93 if self.received[pos] {
94 Err(Error::DuplicateOriginalShardIndex { index })
95 } else if original_shard.len() != self.shard_bytes {
96 Err(Error::DifferentShardSize {
97 shard_bytes: self.shard_bytes,
98 got: original_shard.len(),
99 })
100 } else {
101 self.shards.insert(pos, original_shard);
102
103 self.original_received_count += 1;
104 self.received.set(pos, true);
105 Ok(())
106 }
107 }
108
109 pub(crate) fn add_recovery_shard<T: AsRef<[u8]>>(
114 &mut self,
115 index: usize,
116 recovery_shard: T,
117 ) -> Result<(), Error> {
118 if index >= self.recovery_count {
119 return Err(Error::InvalidRecoveryShardIndex {
120 recovery_count: self.recovery_count,
121 index,
122 });
123 }
124
125 let pos = self.recovery_base_pos + index;
126 let recovery_shard = recovery_shard.as_ref();
127
128 if self.received[pos] {
129 Err(Error::DuplicateRecoveryShardIndex { index })
130 } else if recovery_shard.len() != self.shard_bytes {
131 Err(Error::DifferentShardSize {
132 shard_bytes: self.shard_bytes,
133 got: recovery_shard.len(),
134 })
135 } else {
136 self.shards.insert(pos, recovery_shard);
137
138 self.recovery_received_count += 1;
139 self.received.set(pos, true);
140 Ok(())
141 }
142 }
143
144 pub(crate) fn decode_begin(
152 &mut self,
153 ) -> Result<Option<(ShardsRefMut<'_>, usize, usize, &FixedBitSet)>, Error> {
154 if self.original_received_count + self.recovery_received_count < self.original_count {
155 Err(Error::NotEnoughShards {
156 original_count: self.original_count,
157 original_received_count: self.original_received_count,
158 recovery_received_count: self.recovery_received_count,
159 })
160 } else if self.original_received_count == self.original_count {
161 Ok(None)
162 } else {
163 Ok(Some((
164 self.shards.as_ref_mut(),
165 self.original_count,
166 self.recovery_count,
167 &self.received,
168 )))
169 }
170 }
171
172 pub(crate) const fn original_count(&self) -> usize {
174 self.original_count
175 }
176
177 pub(crate) fn reset(
187 &mut self,
188 original_count: usize,
189 recovery_count: usize,
190 shard_bytes: usize,
191
192 original_base_pos: usize,
193 recovery_base_pos: usize,
194 work_count: usize,
195 ) {
196 assert!(shard_bytes.is_multiple_of(2));
197
198 self.original_count = original_count;
199 self.recovery_count = recovery_count;
200 self.shard_bytes = shard_bytes;
201
202 self.original_base_pos = original_base_pos;
203 self.recovery_base_pos = recovery_base_pos;
204
205 self.original_received_count = 0;
206 self.recovery_received_count = 0;
207
208 let max_received_pos = core::cmp::max(
209 original_base_pos + original_count,
210 recovery_base_pos + recovery_count,
211 );
212
213 self.received.clear();
214 if self.received.len() < max_received_pos {
215 self.received.grow(max_received_pos);
216 }
217
218 self.shards
219 .resize(work_count, shard_bytes.div_ceil(SHARD_CHUNK_BYTES));
220 }
221
222 pub(crate) fn reset_received(&mut self) {
224 self.original_received_count = 0;
225 self.recovery_received_count = 0;
226 self.received.clear();
227 }
228
229 pub(crate) fn original(&self, index: usize) -> Option<&[u8]> {
234 if index >= self.original_count {
235 return None;
236 }
237 let pos = self.original_base_pos + index;
238
239 if !self.received[pos] {
240 Some(&self.shards[pos].as_flattened()[..self.shard_bytes])
241 } else {
242 None
243 }
244 }
245
246 pub(crate) fn recovery(&self, index: usize) -> Option<&[u8]> {
253 if index >= self.recovery_count {
254 return None;
255 }
256 let pos = self.recovery_base_pos + index;
257
258 if self.missing_original_count() > 0 && !self.received[pos] {
259 Some(&self.shards[pos].as_flattened()[..self.shard_bytes])
260 } else {
261 None
262 }
263 }
264
265 pub(crate) fn undo_last_chunk_encoding(&mut self) {
267 self.shards.undo_last_chunk_encoding(
268 self.shard_bytes,
269 self.original_base_pos..self.original_base_pos + self.original_count,
270 );
271 }
272
273 pub(crate) fn undo_last_chunk_encoding_recovery(&mut self) {
277 self.shards.undo_last_chunk_encoding(
278 self.shard_bytes,
279 self.recovery_base_pos..self.recovery_base_pos + self.recovery_count,
280 );
281 }
282
283 pub(crate) const fn missing_original_count(&self) -> usize {
285 self.original_count - self.original_received_count
286 }
287
288 pub(crate) const fn recovery_count(&self) -> usize {
290 self.recovery_count
291 }
292
293 pub(crate) const fn missing_recovery_count(&self) -> usize {
295 self.recovery_count - self.recovery_received_count
296 }
297}