structured_asic/references/
checks.rs1use super::{
4 Access, AccessError, Agreement, ArchiveError, Catalogue, CheckError, DigestProvider,
5 DigestReading, OwnedBinaryXml, RangeSource, Unchecked, VisitError, Work, binary, digest,
6 resource,
7};
8use structured_asn1::memory::OwnedVec;
9
10pub struct Checks<'c, 'v, 's, N, D, G, S: RangeSource> {
13 catalogue: &'c Catalogue<'v, 's, N, D, G, S>,
14 answers: OwnedVec<DigestReading>,
15}
16impl<'c, 'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource>
17 Checks<'c, 'v, 's, N, D, G, S>
18{
19 #[must_use]
21 pub fn catalogue(&self) -> &'c Catalogue<'v, 's, N, D, G, S> {
22 self.catalogue
23 }
24 #[must_use]
26 pub fn answers(&self) -> &[DigestReading] {
27 &self.answers
28 }
29 pub(crate) fn into_answers(self) -> OwnedVec<DigestReading> {
30 self.answers
31 }
32 pub fn all_match<W: Work>(&self, work: &W) -> Result<bool, ArchiveError<S::Error>> {
34 let execution = self.catalogue.base().session().query(work);
35 self.catalogue
36 .base()
37 .archive()
38 .admit(&execution, self.catalogue.base().central_span())?;
39 if self.answers.is_empty() {
40 return Ok(false);
41 }
42 for answer in &self.answers {
43 execution
44 .charge_work(1)
45 .map_err(|cause| resource(self.catalogue.base().central_span(), cause))?;
46 if *answer != DigestReading::Checked(Agreement::Matches) {
47 return Ok(false);
48 }
49 }
50 Ok(true)
51 }
52}
53
54struct Group<D, G, W: Work> {
55 member: crate::zip::MemberId<D, G>,
56 references: OwnedVec<usize, W>,
57}
58
59impl<'v, 's, N: Copy + Eq, D: Copy + Eq, G: Copy + Eq, S: RangeSource>
60 Catalogue<'v, 's, N, D, G, S>
61{
62 #[expect(
71 clippy::too_many_lines,
72 reason = "all original methods and sources must finish preflight before any digest initialization; the two execution phases share the same funded plan"
73 )]
74 pub async fn check_objects<'p, X, P, W: Work>(
75 &self,
76 xml: &X,
77 provider: &'p P,
78 work: &W,
79 ) -> Result<
80 Checks<'_, 'v, 's, N, D, G, S>,
81 CheckError<X::Error, P::Error, N, D, G, S::Error, S::Document, S::Generation>,
82 >
83 where
84 X: OwnedBinaryXml<Id = N, Document = D, Generation = G>,
85 P: DigestProvider<'p, X>,
86 {
87 let execution = self.base().session().query(work);
88 let at = self.base().central_span();
89 let archive_error =
90 |cause| CheckError::Reference(AccessError::Xml(VisitError::Visitor(cause)));
91 let refusal = |cause| archive_error(resource(at, cause));
92 self.base()
93 .archive()
94 .admit(&execution, at)
95 .map_err(archive_error)?;
96 let access = Access::new(xml, self.reading().snapshot(), &execution);
97 let fence = || {
98 access
99 .inspect(self.reading().root(), |_| ())
100 .map_err(|cause| {
101 CheckError::Reference(match cause {
102 AccessError::Xml(cause) => AccessError::Xml(VisitError::Processing(cause)),
103 AccessError::Changed { node } => AccessError::Changed { node },
104 AccessError::Resource { node, cause } => {
105 AccessError::Resource { node, cause }
106 }
107 })
108 })
109 };
110 fence()?;
111 let mut answers = execution.vector();
112 let mut prepared = execution.vector();
113 answers
114 .try_reserve_exact(self.objects().len())
115 .map_err(refusal)?;
116 prepared
117 .try_reserve_exact(self.objects().len())
118 .map_err(refusal)?;
119 let mut groups = execution.vector();
120 let mut by_member = execution.map();
121 for (index, object) in self.reading().objects().iter().enumerate() {
122 let instruction = match (object.method, object.value) {
123 (crate::manifest::Slot::One(method), crate::manifest::Slot::One(value)) => Some(
124 digest::prepare_xml(
125 xml,
126 digest::Reference {
127 snapshot: self.reading().snapshot(),
128 method,
129 value,
130 },
131 provider,
132 &execution,
133 )
134 .map_err(|cause| {
135 CheckError::Digest(
136 cause.map_callback(|cause| cause.map_source(|never| match never {})),
137 )
138 })?,
139 ),
140 _ => None,
141 };
142 let mut answer = match (object.method, object.value) {
143 (crate::manifest::Slot::One(_), crate::manifest::Slot::One(_)) => {
144 DigestReading::Unchecked(Unchecked::Input)
145 }
146 (crate::manifest::Slot::One(_), _) => DigestReading::Unchecked(Unchecked::Value),
147 _ => DigestReading::Unchecked(Unchecked::Method),
148 };
149 if instruction.is_some()
150 && let Some(member) = self
151 .object_member(index, &execution)
152 .map_err(archive_error)?
153 {
154 member.source(&execution).map_err(CheckError::Input)?;
155 let group = if let Some(index) = by_member
156 .get(&member.id().ordinal(), &execution)
157 .map_err(refusal)?
158 {
159 *index
160 } else {
161 let index = groups.len();
162 groups
163 .try_push(Group {
164 member: member.id(),
165 references: execution.vector(),
166 })
167 .map_err(refusal)?;
168 by_member
169 .try_insert(member.id().ordinal(), index)
170 .map_err(refusal)?;
171 index
172 };
173 groups[group].references.try_push(index).map_err(refusal)?;
174 answer = DigestReading::Unchecked(Unchecked::NotExecuted);
175 }
176 prepared.try_push(instruction).map_err(refusal)?;
177 answers.try_push(answer).map_err(refusal)?;
178 }
179 fence()?;
180 self.base()
181 .archive()
182 .admit(&execution, at)
183 .map_err(archive_error)?;
184 for group in groups {
185 let mut instructions = execution.vector();
186 instructions
187 .try_reserve_exact(group.references.len())
188 .map_err(refusal)?;
189 for index in &group.references {
190 execution.charge_work(1).map_err(refusal)?;
191 instructions
192 .try_push(
193 prepared[*index]
194 .take()
195 .expect("each original reference belongs to exactly one source group"),
196 )
197 .map_err(refusal)?;
198 }
199 let member = self
200 .base()
201 .archive()
202 .original(group.member, &execution)
203 .map_err(archive_error)?;
204 let mut source = member.source(&execution).map_err(CheckError::Input)?;
205 let location = binary::Location {
206 snapshot: self.reading().snapshot(),
207 node: self.reading().objects()[group.references[0]].node,
208 };
209 let batch = digest::Batch::prepare(provider, instructions.into_owned(), &execution)
210 .map_err(|cause| {
211 CheckError::Digest(binary::Error::Callback {
212 location,
213 cause: cause.map_source(|never| match never {}),
214 })
215 })?;
216 let result = batch
217 .run(xml, &mut source, &execution)
218 .await
219 .map_err(CheckError::Digest)?;
220 for (index, answer) in group.references.iter().zip(&*result) {
221 execution.charge_work(1).map_err(refusal)?;
222 answers[*index] = DigestReading::Checked(*answer);
223 }
224 }
225 self.base()
226 .archive()
227 .admit(&execution, at)
228 .map_err(archive_error)?;
229 fence()?;
230 Ok(Checks {
231 catalogue: self,
232 answers: answers.into_owned(),
233 })
234 }
235}