1use super::{
4 Access, Agreement, Digest, Error, Location, OwnedVec, Prepared, PreparedValue, Reference,
5 StreamError, Work, Xml, binary, compare_resolved_finished, stream,
6};
7use structured_cms::digest::{DigestBatch, DigestError};
8
9enum Value {
10 Answer(Agreement),
11 Request {
12 ordinal: usize,
13 expected: OwnedVec<u8>,
14 },
15}
16struct Declared<N, D, G> {
17 reference: Reference<N, D, G>,
18 value: Value,
19}
20
21pub struct Batch<'p, P: Digest<'p>, N, D, G> {
25 provider: &'p P,
26 declarations: OwnedVec<Declared<N, D, G>>,
27 native: Option<DigestBatch<'p, P>>,
28}
29
30fn map_error<SE, PE>(cause: DigestError<PE>) -> Error<SE, PE> {
31 match cause {
32 DigestError::Processing(cause) => Error::Stream(cause),
33 DigestError::Callback(cause) => Error::Provider(cause),
34 DigestError::OutputWidth { expected, actual } => Error::OutputWidth { expected, actual },
35 DigestError::Unstreamable => Error::Unstreamable,
36 }
37}
38
39impl<'p, P: Digest<'p>, N: Copy + Eq, D: Copy + Eq, G: Copy + Eq> Batch<'p, P, N, D, G> {
40 pub fn prepare<W: Work>(
44 provider: &'p P,
45 prepared: OwnedVec<Prepared<'p, P, N, D, G>>,
46 work: &W,
47 ) -> Result<Self, Error<core::convert::Infallible, P::Error>> {
48 let mut identities = work.vector();
49 let mut declarations = work.vector();
50 identities
51 .try_reserve_exact(prepared.len())
52 .map_err(Error::Resource)?;
53 declarations
54 .try_reserve_exact(prepared.len())
55 .map_err(Error::Resource)?;
56 work.charge_work(core::mem::size_of_val(&*prepared) as u64)
57 .map_err(Error::Resource)?;
58 for prepared in prepared {
59 if !core::ptr::eq(prepared.provider, provider) {
60 return Err(Error::ForeignDigest);
61 }
62 let value = match prepared.value {
63 PreparedValue::Answer(answer) => Value::Answer(answer),
64 PreparedValue::Ready { identity, expected } => {
65 let ordinal = identities.len();
66 identities.try_push(identity).map_err(Error::Resource)?;
67 Value::Request { ordinal, expected }
68 }
69 };
70 declarations
71 .try_push(Declared {
72 reference: prepared.reference,
73 value,
74 })
75 .map_err(Error::Resource)?;
76 }
77 let batch = if identities.is_empty() {
78 None
79 } else {
80 Some(
81 DigestBatch::prepare(
82 provider,
83 identities.into_owned(),
84 work,
85 "xml.digest.batch",
86 0,
87 )
88 .map_err(map_error)?,
89 )
90 };
91 Ok(Self {
92 provider,
93 declarations: declarations.into_owned(),
94 native: batch,
95 })
96 }
97
98 pub async fn run<
107 X: Xml<Id = N, Document = D, Generation = G>,
108 S: stream::AsyncSource,
109 W: Work,
110 >(
111 self,
112 xml: &X,
113 source: &mut S,
114 work: &W,
115 ) -> binary::BinaryResult<OwnedVec<Agreement>, X, Error<S::Error, P::Error>> {
116 let mut answers = work.vector();
117 for declared in &self.declarations {
118 let location = Location {
119 snapshot: declared.reference.snapshot,
120 node: declared.reference.value,
121 };
122 Access::new(xml, location.snapshot, work)
123 .inspect(location.node, |_| ())
124 .map_err(|cause| binary::Error::Access { location, cause })?;
125 }
126 let Some(first) = self.declarations.first() else {
127 return Ok(answers.into_owned());
128 };
129 let location = Location {
130 snapshot: first.reference.snapshot,
131 node: first.reference.value,
132 };
133 answers
134 .try_reserve_exact(self.declarations.len())
135 .map_err(|cause| binary::Error::Callback {
136 location,
137 cause: Error::Resource(cause),
138 })?;
139 let memo = match self.native {
140 Some(batch) => Some(batch.run_async(source, work).await.map_err(|cause| {
141 binary::Error::Callback {
142 location,
143 cause: match cause {
144 StreamError::Source(cause) => Error::Source(cause),
145 StreamError::Callback(cause) => map_error(cause),
146 StreamError::Processing(cause) => Error::Stream(cause),
147 },
148 }
149 })?),
150 None => None,
151 };
152 for declared in self.declarations {
153 let access = Access::new(xml, declared.reference.snapshot, work);
154 let location = Location {
155 snapshot: declared.reference.snapshot,
156 node: declared.reference.value,
157 };
158 access
159 .inspect(location.node, |_| ())
160 .map_err(|cause| binary::Error::Access { location, cause })?;
161 let answer = match declared.value {
162 Value::Answer(answer) => answer,
163 Value::Request { ordinal, expected } => {
164 let actual = memo
165 .as_ref()
166 .expect("ready declarations initialize their private batch")
167 .for_request(ordinal, work)
168 .map_err(|cause| binary::Error::Callback {
169 location,
170 cause: map_error(cause),
171 })?
172 .expect("each ready declaration has the original private request mapping");
173 compare_resolved_finished(
177 &access,
178 declared.reference,
179 self.provider,
180 actual.identity(),
181 actual.finished(),
182 &expected,
183 )
184 .map_err(|cause| {
185 cause.map_callback(|cause| cause.map_source(|never| match never {}))
186 })?
187 }
188 };
189 answers
190 .try_push(answer)
191 .map_err(|cause| binary::Error::Callback {
192 location,
193 cause: Error::Resource(cause),
194 })?;
195 }
196 Ok(answers.into_owned())
197 }
198}