1use crate::Error;
4use crate::provider::{Finalized, Finished, Prehash};
5use crate::stream::{self, Sink, Source, StreamError};
6use core::cmp::Ordering;
7use structured_asn1::digest::{Digest, Resolution};
8use structured_asn1::memory::{OwnedVec, SortError, Work};
9
10pub type PassError<SE, PE> = StreamError<SE, DigestError<PE>>;
12pub type DigestOutput<'a, P> =
14 Finalized<'a, P, <P as Digest<'a>>::Identity, <P as Digest<'a>>::Output>;
15pub type DigestResult<T, E> = Result<T, DigestError<E>>;
17pub type PassResult<T, SE, PE> = Result<T, PassError<SE, PE>>;
19
20#[derive(Debug)]
22pub enum DigestError<E> {
23 Processing(Error),
25 Callback(E),
27 OutputWidth {
30 expected: usize,
32 actual: usize,
34 },
35 Unstreamable,
39}
40
41fn started<S, E>(start: Result<Resolution<S>, E>) -> Result<S, DigestError<E>> {
42 match start.map_err(DigestError::Callback)? {
43 Resolution::Supported(state) => Ok(state),
44 Resolution::Unsupported | Resolution::Malformed => Err(DigestError::Unstreamable),
45 }
46}
47
48struct Pending<I, S> {
49 identity: I,
50 state: S,
51}
52
53struct Request<I> {
54 ordinal: usize,
55 identity: I,
56}
57
58pub struct DigestBatch<'a, P: Digest<'a>> {
60 provider: &'a P,
61 pending: OwnedVec<Pending<P::Identity, P::State>>,
62 mapping: OwnedVec<usize>,
63 field: &'static str,
64 at: u64,
65}
66
67impl<'a, P: Digest<'a>> DigestBatch<'a, P> {
68 pub fn prepare<W: Work>(
75 provider: &'a P,
76 identities: OwnedVec<P::Identity>,
77 execution: &W,
78 field: &'static str,
79 at: u64,
80 ) -> Result<Self, DigestError<P::Error>> {
81 crate::provider::require_instance(provider, field, at).map_err(DigestError::Processing)?;
82 let refusal = |reason| DigestError::Processing(Error::resource(reason, field, at));
83 execution.charge_work(1).map_err(refusal)?;
84 let count = identities.len();
85 let mut ordered = execution.vector();
86 ordered
87 .try_reserve_exact(identities.len())
88 .map_err(refusal)?;
89 execution
90 .charge_work(core::mem::size_of_val(&*identities) as u64)
91 .map_err(refusal)?;
92 for (ordinal, identity) in identities.into_iter().enumerate() {
93 ordered
94 .try_push(Request { ordinal, identity })
95 .map_err(refusal)?;
96 }
97 ordered
98 .try_sort_unstable_by(|left, right| {
99 provider.compare(&left.identity, &right.identity, execution)
100 })
101 .map_err(|error| match error {
102 SortError::Resource(reason) => refusal(reason),
103 SortError::Comparison(error) => DigestError::Callback(error),
104 })?;
105 let mut pending: OwnedVec<Pending<P::Identity, P::State>, _> = execution.vector();
106 let mut mapping = execution.vector();
107 mapping.try_reserve_exact(count).map_err(refusal)?;
108 for _ in 0..count {
111 mapping.try_push(0).map_err(refusal)?;
112 }
113 execution
114 .charge_work(core::mem::size_of_val(&*ordered) as u64)
115 .map_err(refusal)?;
116 for Request { ordinal, identity } in ordered.into_owned() {
117 execution.charge_work(1).map_err(refusal)?;
118 if let Some(previous) = pending.last()
119 && provider
120 .compare(&previous.identity, &identity, execution)
121 .map_err(DigestError::Callback)?
122 == Ordering::Equal
123 {
124 mapping[ordinal] = pending.len() - 1;
125 continue;
126 }
127 pending.try_reserve(1).map_err(refusal)?;
128 let state = started(provider.start(&identity, execution))?;
129 pending
130 .try_push(Pending { identity, state })
131 .map_err(refusal)?;
132 mapping[ordinal] = pending.len() - 1;
133 }
134 Ok(Self {
135 provider,
136 pending: pending.into_owned(),
137 mapping: mapping.into_owned(),
138 field,
139 at,
140 })
141 }
142
143 #[must_use]
145 pub fn len(&self) -> usize {
146 self.pending.len()
147 }
148 #[must_use]
150 pub fn is_empty(&self) -> bool {
151 self.pending.is_empty()
152 }
153
154 pub fn run<S: Source, W: Work>(
160 mut self,
161 source: &mut S,
162 execution: &W,
163 ) -> Result<DigestMemo<'a, P>, PassError<S::Error, P::Error>> {
164 let field = self.field;
165 let at = self.at;
166 let mut outputs = execution.vector();
167 outputs
168 .try_reserve_exact(self.pending.len())
169 .map_err(|reason| StreamError::Processing(Error::resource(reason, field, at)))?;
170 stream::run(source, &mut BatchSink(&mut self), execution, field, at)?;
171 self.finish(outputs, execution)
172 .map_err(StreamError::Callback)
173 }
174
175 pub(crate) fn feed<W: Work>(
176 &mut self,
177 bytes: &[u8],
178 execution: &W,
179 ) -> Result<(), DigestError<P::Error>> {
180 for pending in &mut self.pending {
181 execution.charge_work(1).map_err(|reason| {
182 DigestError::Processing(Error::resource(reason, self.field, self.at))
183 })?;
184 self.provider
185 .update(&mut pending.state, bytes, execution)
186 .map_err(DigestError::Callback)?;
187 }
188 Ok(())
189 }
190
191 pub(crate) fn finish<W: Work>(
192 self,
193 mut outputs: OwnedVec<DigestOutput<'a, P>, W>,
194 execution: &W,
195 ) -> Result<DigestMemo<'a, P>, DigestError<P::Error>> {
196 let refusal =
197 |reason| DigestError::Processing(Error::resource(reason, self.field, self.at));
198 execution
199 .charge_work(core::mem::size_of_val(&*self.pending) as u64)
200 .map_err(refusal)?;
201 for pending in self.pending {
202 execution.charge_work(1).map_err(refusal)?;
203 let output = self
204 .provider
205 .finish(pending.state, execution)
206 .map_err(DigestError::Callback)?;
207 if let Some(expected) = self.provider.width(&pending.identity) {
208 let actual = output.as_ref().len();
209 if actual != expected {
210 return Err(DigestError::OutputWidth { expected, actual });
211 }
212 }
213 outputs
214 .try_push(Finalized::new(self.provider, pending.identity, output))
215 .map_err(refusal)?;
216 }
217 Ok(DigestMemo {
218 provider: self.provider,
219 outputs: outputs.into_owned(),
220 mapping: self.mapping,
221 field: self.field,
222 at: self.at,
223 })
224 }
225
226 pub(crate) fn provider(&self) -> &'a P {
227 self.provider
228 }
229
230 pub(crate) fn location(&self) -> (&'static str, u64) {
231 (self.field, self.at)
232 }
233}
234
235struct BatchSink<'s, 'a, P: Digest<'a>>(&'s mut DigestBatch<'a, P>);
236
237impl<'a, P: Digest<'a>> Sink for BatchSink<'_, 'a, P> {
238 type Error = DigestError<P::Error>;
239 fn write<W: Work>(&mut self, bytes: &[u8], execution: &W) -> Result<(), Self::Error> {
240 self.0.feed(bytes, execution)
241 }
242}
243
244pub struct DigestMemo<'a, P: Digest<'a>> {
246 provider: &'a P,
247 outputs: OwnedVec<DigestOutput<'a, P>>,
248 mapping: OwnedVec<usize>,
249 field: &'static str,
250 at: u64,
251}
252
253pub struct RelatedDigest<'m, 'a, P: Digest<'a>> {
259 provider: &'a P,
260 identity: &'m P::Identity,
261 output: P::Output,
262}
263
264impl<'a, P: Digest<'a>> RelatedDigest<'_, 'a, P> {
265 #[must_use]
267 pub fn prehash(&self) -> Prehash<'_, '_, P, P::Identity> {
268 Prehash::new(self.provider, self.identity, self.output.as_ref())
269 }
270
271 #[must_use]
273 pub fn finished(&self) -> Finished<'_, P, P::Identity, P::Output> {
274 Finished::new(self.provider, self.identity, &self.output)
275 }
276}
277
278struct RelatedSink<'s, 'a, P: Digest<'a>> {
279 provider: &'a P,
280 state: &'s mut P::State,
281}
282
283impl<'a, P: Digest<'a>> Sink for RelatedSink<'_, 'a, P> {
284 type Error = DigestError<P::Error>;
285
286 fn write<W: Work>(&mut self, bytes: &[u8], execution: &W) -> Result<(), Self::Error> {
287 self.provider
288 .update(self.state, bytes, execution)
289 .map_err(DigestError::Callback)
290 }
291}
292
293impl<'a, P: Digest<'a>> DigestMemo<'a, P> {
294 pub fn digest_related<'m, S: Source, W: Work>(
302 &'m self,
303 ordinal: usize,
304 source: &mut S,
305 execution: &W,
306 field: &'static str,
307 at: u64,
308 ) -> PassResult<Option<RelatedDigest<'m, 'a, P>>, S::Error, P::Error> {
309 let Some(existing) = self
310 .for_request(ordinal, execution)
311 .map_err(StreamError::Callback)?
312 else {
313 return Ok(None);
314 };
315 let identity = existing.identity();
316 let mut state =
317 started(self.provider.start(identity, execution)).map_err(StreamError::Callback)?;
318 stream::run(
319 source,
320 &mut RelatedSink {
321 provider: self.provider,
322 state: &mut state,
323 },
324 execution,
325 field,
326 at,
327 )?;
328 let output = self
329 .provider
330 .finish(state, execution)
331 .map_err(|error| StreamError::Callback(DigestError::Callback(error)))?;
332 if let Some(expected) = self.provider.width(identity) {
333 let actual = output.as_ref().len();
334 if actual != expected {
335 return Err(StreamError::Callback(DigestError::OutputWidth {
336 expected,
337 actual,
338 }));
339 }
340 }
341 Ok(Some(RelatedDigest {
342 provider: self.provider,
343 identity,
344 output,
345 }))
346 }
347
348 #[must_use]
350 pub fn len(&self) -> usize {
351 self.outputs.len()
352 }
353 #[must_use]
355 pub fn is_empty(&self) -> bool {
356 self.outputs.is_empty()
357 }
358
359 #[must_use]
361 pub fn request_count(&self) -> usize {
362 self.mapping.len()
363 }
364
365 pub fn for_request<W: Work>(
371 &self,
372 ordinal: usize,
373 execution: &W,
374 ) -> DigestResult<Option<&DigestOutput<'a, P>>, P::Error> {
375 execution.charge_work(1).map_err(|reason| {
376 DigestError::Processing(Error::resource(reason, self.field, self.at))
377 })?;
378 let Some(index) = self.mapping.get(ordinal) else {
379 return Ok(None);
380 };
381 debug_assert!(*index < self.outputs.len());
384 Ok(Some(&self.outputs[*index]))
385 }
386
387 pub fn find<W: Work>(
392 &self,
393 identity: &P::Identity,
394 execution: &W,
395 ) -> DigestResult<Option<&DigestOutput<'a, P>>, P::Error> {
396 let refusal =
397 |reason| DigestError::Processing(Error::resource(reason, self.field, self.at));
398 execution.charge_work(1).map_err(refusal)?;
399 let mut from = 0;
400 let mut to = self.outputs.len();
401 while from < to {
402 execution.charge_work(1).map_err(refusal)?;
403 let middle = from + (to - from) / 2;
404 let held = &self.outputs[middle];
405 match self
406 .provider
407 .compare(held.identity(), identity, execution)
408 .map_err(DigestError::Callback)?
409 {
410 Ordering::Equal => return Ok(Some(held)),
411 Ordering::Less => from = middle + 1,
412 Ordering::Greater => to = middle,
413 }
414 }
415 Ok(None)
416 }
417}