1use crate::Error;
4use crate::provider::{DigestProvider, Finalized, Prehash};
5use crate::stream::{self, Sink, Source, StreamError};
6use core::cmp::Ordering;
7use structured_asn1::memory::{OwnedVec, SortError, Work};
8
9pub type PassError<SE, PE> = StreamError<SE, DigestError<PE>>;
11pub type DigestOutput<'a, P> =
13 Finalized<'a, P, <P as DigestProvider<'a>>::Identity, <P as DigestProvider<'a>>::Output>;
14pub type DigestResult<T, E> = Result<T, DigestError<E>>;
16pub type PassResult<T, SE, PE> = Result<T, PassError<SE, PE>>;
18
19#[derive(Debug)]
21pub enum DigestError<E> {
22 Processing(Error),
24 Callback(E),
26 OutputWidth {
29 expected: usize,
31 actual: usize,
33 },
34}
35
36struct Pending<I, S> {
37 identity: I,
38 state: S,
39}
40
41struct Request<I> {
42 ordinal: usize,
43 identity: I,
44}
45
46pub struct DigestBatch<'a, P: DigestProvider<'a>> {
48 provider: &'a P,
49 pending: OwnedVec<Pending<P::Identity, P::State>>,
50 mapping: OwnedVec<usize>,
51 field: &'static str,
52 at: u64,
53}
54
55impl<'a, P: DigestProvider<'a>> DigestBatch<'a, P> {
56 pub fn prepare<W: Work>(
63 provider: &'a P,
64 identities: OwnedVec<P::Identity>,
65 execution: &W,
66 field: &'static str,
67 at: u64,
68 ) -> Result<Self, DigestError<P::Error>> {
69 crate::provider::require_instance(provider, field, at).map_err(DigestError::Processing)?;
70 let refusal = |reason| DigestError::Processing(Error::resource(reason, field, at));
71 execution.charge_work(1).map_err(refusal)?;
72 let count = identities.len();
73 let mut ordered = execution.vector();
74 ordered
75 .try_reserve_exact(identities.len())
76 .map_err(refusal)?;
77 execution
78 .charge_work(core::mem::size_of_val(&*identities) as u64)
79 .map_err(refusal)?;
80 for (ordinal, identity) in identities.into_iter().enumerate() {
81 ordered
82 .try_push(Request { ordinal, identity })
83 .map_err(refusal)?;
84 }
85 ordered
86 .try_sort_unstable_by(|left, right| {
87 provider.compare_digest(&left.identity, &right.identity, execution)
88 })
89 .map_err(|error| match error {
90 SortError::Resource(reason) => refusal(reason),
91 SortError::Comparison(error) => DigestError::Callback(error),
92 })?;
93 let mut pending: OwnedVec<Pending<P::Identity, P::State>, _> = execution.vector();
94 let mut mapping = execution.vector();
95 mapping.try_reserve_exact(count).map_err(refusal)?;
96 for _ in 0..count {
99 mapping.try_push(0).map_err(refusal)?;
100 }
101 execution
102 .charge_work(core::mem::size_of_val(&*ordered) as u64)
103 .map_err(refusal)?;
104 for Request { ordinal, identity } in ordered.into_owned() {
105 execution.charge_work(1).map_err(refusal)?;
106 if let Some(previous) = pending.last()
107 && provider
108 .compare_digest(&previous.identity, &identity, execution)
109 .map_err(DigestError::Callback)?
110 == Ordering::Equal
111 {
112 mapping[ordinal] = pending.len() - 1;
113 continue;
114 }
115 pending.try_reserve(1).map_err(refusal)?;
116 let state = provider
117 .start_digest(&identity, execution)
118 .map_err(DigestError::Callback)?;
119 pending
120 .try_push(Pending { identity, state })
121 .map_err(refusal)?;
122 mapping[ordinal] = pending.len() - 1;
123 }
124 Ok(Self {
125 provider,
126 pending: pending.into_owned(),
127 mapping: mapping.into_owned(),
128 field,
129 at,
130 })
131 }
132
133 #[must_use]
135 pub fn len(&self) -> usize {
136 self.pending.len()
137 }
138 #[must_use]
140 pub fn is_empty(&self) -> bool {
141 self.pending.is_empty()
142 }
143
144 pub fn run<S: Source, W: Work>(
150 mut self,
151 source: &mut S,
152 execution: &W,
153 ) -> Result<DigestMemo<'a, P>, PassError<S::Error, P::Error>> {
154 let field = self.field;
155 let at = self.at;
156 let mut outputs = execution.vector();
157 outputs
158 .try_reserve_exact(self.pending.len())
159 .map_err(|reason| StreamError::Processing(Error::resource(reason, field, at)))?;
160 stream::run(source, &mut BatchSink(&mut self), execution, field, at)?;
161 self.finish(outputs, execution)
162 .map_err(StreamError::Callback)
163 }
164
165 pub(crate) fn feed<W: Work>(
166 &mut self,
167 bytes: &[u8],
168 execution: &W,
169 ) -> Result<(), DigestError<P::Error>> {
170 for pending in &mut self.pending {
171 execution.charge_work(1).map_err(|reason| {
172 DigestError::Processing(Error::resource(reason, self.field, self.at))
173 })?;
174 self.provider
175 .update_digest(&mut pending.state, bytes, execution)
176 .map_err(DigestError::Callback)?;
177 }
178 Ok(())
179 }
180
181 pub(crate) fn finish<W: Work>(
182 self,
183 mut outputs: OwnedVec<DigestOutput<'a, P>, W>,
184 execution: &W,
185 ) -> Result<DigestMemo<'a, P>, DigestError<P::Error>> {
186 let refusal =
187 |reason| DigestError::Processing(Error::resource(reason, self.field, self.at));
188 execution
189 .charge_work(core::mem::size_of_val(&*self.pending) as u64)
190 .map_err(refusal)?;
191 for pending in self.pending {
192 execution.charge_work(1).map_err(refusal)?;
193 let output = self
194 .provider
195 .finish_digest(pending.state, execution)
196 .map_err(DigestError::Callback)?;
197 if let Some(expected) = self.provider.digest_width(&pending.identity) {
198 let actual = output.as_ref().len();
199 if actual != expected {
200 return Err(DigestError::OutputWidth { expected, actual });
201 }
202 }
203 outputs
204 .try_push(Finalized::new(self.provider, pending.identity, output))
205 .map_err(refusal)?;
206 }
207 Ok(DigestMemo {
208 provider: self.provider,
209 outputs: outputs.into_owned(),
210 mapping: self.mapping,
211 field: self.field,
212 at: self.at,
213 })
214 }
215
216 pub(crate) fn provider(&self) -> &'a P {
217 self.provider
218 }
219
220 pub(crate) fn location(&self) -> (&'static str, u64) {
221 (self.field, self.at)
222 }
223}
224
225struct BatchSink<'s, 'a, P: DigestProvider<'a>>(&'s mut DigestBatch<'a, P>);
226
227impl<'a, P: DigestProvider<'a>> Sink for BatchSink<'_, 'a, P> {
228 type Error = DigestError<P::Error>;
229 fn write<W: Work>(&mut self, bytes: &[u8], execution: &W) -> Result<(), Self::Error> {
230 self.0.feed(bytes, execution)
231 }
232}
233
234pub struct DigestMemo<'a, P: DigestProvider<'a>> {
236 provider: &'a P,
237 outputs: OwnedVec<DigestOutput<'a, P>>,
238 mapping: OwnedVec<usize>,
239 field: &'static str,
240 at: u64,
241}
242
243pub struct RelatedDigest<'m, 'a, P: DigestProvider<'a>> {
249 provider: &'a P,
250 identity: &'m P::Identity,
251 output: P::Output,
252}
253
254impl<'a, P: DigestProvider<'a>> RelatedDigest<'_, 'a, P> {
255 #[must_use]
257 pub fn prehash(&self) -> Prehash<'_, '_, P, P::Identity> {
258 Prehash::new(self.provider, self.identity, self.output.as_ref())
259 }
260}
261
262struct RelatedSink<'s, 'a, P: DigestProvider<'a>> {
263 provider: &'a P,
264 state: &'s mut P::State,
265}
266
267impl<'a, P: DigestProvider<'a>> Sink for RelatedSink<'_, 'a, P> {
268 type Error = DigestError<P::Error>;
269
270 fn write<W: Work>(&mut self, bytes: &[u8], execution: &W) -> Result<(), Self::Error> {
271 self.provider
272 .update_digest(self.state, bytes, execution)
273 .map_err(DigestError::Callback)
274 }
275}
276
277impl<'a, P: DigestProvider<'a>> DigestMemo<'a, P> {
278 pub fn digest_related<'m, S: Source, W: Work>(
286 &'m self,
287 ordinal: usize,
288 source: &mut S,
289 execution: &W,
290 field: &'static str,
291 at: u64,
292 ) -> PassResult<Option<RelatedDigest<'m, 'a, P>>, S::Error, P::Error> {
293 let Some(existing) = self
294 .for_request(ordinal, execution)
295 .map_err(StreamError::Callback)?
296 else {
297 return Ok(None);
298 };
299 let identity = existing.identity();
300 let mut state = self
301 .provider
302 .start_digest(identity, execution)
303 .map_err(|error| StreamError::Callback(DigestError::Callback(error)))?;
304 stream::run(
305 source,
306 &mut RelatedSink {
307 provider: self.provider,
308 state: &mut state,
309 },
310 execution,
311 field,
312 at,
313 )?;
314 let output = self
315 .provider
316 .finish_digest(state, execution)
317 .map_err(|error| StreamError::Callback(DigestError::Callback(error)))?;
318 if let Some(expected) = self.provider.digest_width(identity) {
319 let actual = output.as_ref().len();
320 if actual != expected {
321 return Err(StreamError::Callback(DigestError::OutputWidth {
322 expected,
323 actual,
324 }));
325 }
326 }
327 Ok(Some(RelatedDigest {
328 provider: self.provider,
329 identity,
330 output,
331 }))
332 }
333
334 #[must_use]
336 pub fn len(&self) -> usize {
337 self.outputs.len()
338 }
339 #[must_use]
341 pub fn is_empty(&self) -> bool {
342 self.outputs.is_empty()
343 }
344
345 #[must_use]
347 pub fn request_count(&self) -> usize {
348 self.mapping.len()
349 }
350
351 pub fn for_request<W: Work>(
357 &self,
358 ordinal: usize,
359 execution: &W,
360 ) -> DigestResult<Option<&DigestOutput<'a, P>>, P::Error> {
361 execution.charge_work(1).map_err(|reason| {
362 DigestError::Processing(Error::resource(reason, self.field, self.at))
363 })?;
364 let Some(index) = self.mapping.get(ordinal) else {
365 return Ok(None);
366 };
367 debug_assert!(*index < self.outputs.len());
370 Ok(Some(&self.outputs[*index]))
371 }
372
373 pub fn find<W: Work>(
378 &self,
379 identity: &P::Identity,
380 execution: &W,
381 ) -> DigestResult<Option<&DigestOutput<'a, P>>, P::Error> {
382 let refusal =
383 |reason| DigestError::Processing(Error::resource(reason, self.field, self.at));
384 execution.charge_work(1).map_err(refusal)?;
385 let mut from = 0;
386 let mut to = self.outputs.len();
387 while from < to {
388 execution.charge_work(1).map_err(refusal)?;
389 let middle = from + (to - from) / 2;
390 let held = &self.outputs[middle];
391 match self
392 .provider
393 .compare_digest(held.identity(), identity, execution)
394 .map_err(DigestError::Callback)?
395 {
396 Ordering::Equal => return Ok(Some(held)),
397 Ordering::Less => from = middle + 1,
398 Ordering::Greater => to = middle,
399 }
400 }
401 Ok(None)
402 }
403}