1use crate::{Error, ErrorCode, Octets, Result};
4use structured_asn1::memory::{OwnedVec, Work};
5use structured_asn1::{Encoding, Reader, Span};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Coordinates {
10 Original,
12 Logical,
14}
15
16#[derive(Debug)]
17enum Ranges {
18 One(Span),
19 Many(OwnedVec<Span>),
20}
21
22#[derive(Debug)]
29pub struct SourceMap {
30 ranges: Ranges,
31 carrier: Span,
32 length: u64,
33 empty_end: u64,
34 field: &'static str,
35}
36
37impl SourceMap {
38 pub fn try_copy_in<W: Work>(&self, work: &W) -> Result<Self> {
43 let fail = |reason| Error::resource(reason, self.field, self.carrier.start);
44 work.charge_work(1).map_err(fail)?;
45 let ranges = match &self.ranges {
46 Ranges::One(span) => Ranges::One(*span),
47 Ranges::Many(spans) => {
48 let mut copy = work.vector();
49 copy.try_extend_from_slice(spans).map_err(fail)?;
50 Ranges::Many(copy.into_owned())
51 }
52 };
53 Ok(Self {
54 ranges,
55 carrier: self.carrier,
56 length: self.length,
57 empty_end: self.empty_end,
58 field: self.field,
59 })
60 }
61
62 pub(crate) fn of(
63 content: &Octets<'_>,
64 parent: &Reader<'_>,
65 field: &'static str,
66 ) -> Result<Self> {
67 let ranges = if let [piece] = content.pieces() {
68 Ranges::One(piece.span())
69 } else {
70 let execution = parent.execution();
71 let refusal = |reason| Error::resource(reason, field, content.span().start);
72 execution
73 .charge_work(content.pieces().len() as u64)
74 .map_err(refusal)?;
75 let mut ranges = execution.vector();
76 ranges
77 .try_reserve_exact(content.pieces().len())
78 .map_err(refusal)?;
79 for piece in content.pieces() {
80 ranges.try_push(piece.span()).map_err(refusal)?;
81 }
82 Ranges::Many(ranges.into_owned())
83 };
84 Ok(Self {
85 ranges,
86 carrier: content.span(),
87 length: content.len() as u64,
88 empty_end: content.content_span().end(),
89 field,
90 })
91 }
92
93 fn mapped(
94 content: &Octets<'_>,
95 parent: &Reader<'_>,
96 field: &'static str,
97 origin: &Self,
98 ) -> Result<Self> {
99 let work = parent.execution();
100 let start = origin.project_offset(content.span().start, &work)?;
101 let end = origin.project_offset(content.span().end(), &work)?;
102 let empty_end = origin.project_offset(content.content_span().end(), &work)?;
103 if origin.coordinates() == Coordinates::Original {
104 return Self::of(content, parent, field);
107 }
108 let carrier = Span {
109 start,
110 length: end - start,
111 };
112 let refusal = |reason| Error::resource(reason, field, carrier.start);
113 work.charge_work(content.pieces().len() as u64)
114 .map_err(refusal)?;
115 let mut ranges = work.vector();
116 for piece in content.pieces() {
117 origin.append_projection(piece.span(), &work, &mut ranges)?;
118 }
119 Ok(Self {
120 ranges: Ranges::Many(ranges.into_owned()),
121 carrier,
122 length: content.len() as u64,
123 empty_end,
124 field,
125 })
126 }
127
128 #[must_use]
130 pub fn coordinates(&self) -> Coordinates {
131 match self.ranges {
132 Ranges::One(_) => Coordinates::Original,
133 Ranges::Many(_) => Coordinates::Logical,
134 }
135 }
136
137 #[must_use]
139 pub fn carrier(&self) -> Span {
140 self.carrier
141 }
142
143 fn ranges(&self) -> &[Span] {
144 match &self.ranges {
145 Ranges::One(span) => core::slice::from_ref(span),
146 Ranges::Many(spans) => spans,
147 }
148 }
149
150 fn logical_offset(&self, offset: u64) -> Result<u64> {
151 match self.ranges {
152 Ranges::One(span) if offset >= span.start && offset <= span.end() => {
153 Ok(offset - span.start)
154 }
155 Ranges::Many(_) if offset <= self.length => Ok(offset),
156 _ => Err(self.invalid()),
157 }
158 }
159
160 fn invalid(&self) -> Error {
161 Error::schema(ErrorCode::InvalidLocation, self.field, self.carrier.start)
162 }
163
164 fn admit(&self, execution: &impl Work) -> Result<()> {
165 execution
166 .charge_work(self.ranges().len() as u64)
167 .map_err(|reason| Error::resource(reason, self.field, self.carrier.start))
168 }
169
170 fn point(&self, logical: u64) -> u64 {
171 let mut base = 0;
172 for span in self.ranges() {
173 if logical < base + span.length {
174 return span.start + logical - base;
175 }
176 base += span.length;
177 }
178 self.ranges()
179 .last()
180 .map_or(self.empty_end, |span| span.end())
181 }
182
183 pub fn project_offset(&self, offset: u64, execution: &impl Work) -> Result<u64> {
190 let logical = self.logical_offset(offset)?;
191 self.admit(execution)?;
192 Ok(self.point(logical))
193 }
194
195 pub fn project_span(&self, span: Span, execution: &impl Work) -> Result<OwnedVec<Span>> {
201 let mut output = execution.vector();
202 self.append_projection(span, execution, &mut output)?;
203 Ok(output.into_owned())
204 }
205
206 fn append_projection<W: Work>(
207 &self,
208 span: Span,
209 execution: &W,
210 output: &mut OwnedVec<Span, W>,
211 ) -> Result<()> {
212 let end = span
213 .start
214 .checked_add(span.length)
215 .ok_or_else(|| self.invalid())?;
216 let start = self.logical_offset(span.start)?;
217 let end = self.logical_offset(end)?;
218 self.admit(execution)?;
219 let refusal = |reason| Error::resource(reason, self.field, self.carrier.start);
220 if start == end {
221 output
222 .try_push(Span {
223 start: self.point(start),
224 length: 0,
225 })
226 .map_err(refusal)?;
227 } else {
228 let mut base = 0;
229 for original in self.ranges() {
230 let next = base + original.length;
231 let low = start.max(base);
232 let high = end.min(next);
233 if low < high {
234 output
235 .try_push(Span {
236 start: original.start + low - base,
237 length: high - low,
238 })
239 .map_err(refusal)?;
240 }
241 base = next;
242 }
243 }
244 Ok(())
245 }
246}
247
248#[derive(Debug)]
250pub enum ContentError<E> {
251 Processing(Error),
253 Decoding {
255 source: Error,
257 context: SourceMap,
259 },
260 Callback {
269 source: E,
271 context: SourceMap,
273 },
274}
275
276pub trait AsyncVisitor {
279 type Output;
281 type Error;
283
284 fn visit<'data: 'scope, 'scope>(
286 &'scope mut self,
287 reader: &'scope mut Reader<'data>,
288 context: &'scope SourceMap,
289 ) -> impl core::future::Future<Output = core::result::Result<Self::Output, Self::Error>>;
290}
291
292enum Carried<'a> {
293 Borrowed(&'a [u8]),
294 Joined(OwnedVec<u8>),
295}
296
297struct Prepared<'a> {
298 carried: Carried<'a>,
299 context: SourceMap,
300 base: u64,
301}
302
303impl Prepared<'_> {
304 fn bytes(&self) -> &[u8] {
305 match &self.carried {
306 Carried::Borrowed(bytes) => bytes,
307 Carried::Joined(bytes) => bytes,
308 }
309 }
310}
311
312impl Octets<'_> {
313 #[expect(
316 clippy::result_large_err,
317 reason = "retained source mapping preserves errors after the awaited join is dropped without allocating an error box"
318 )]
319 pub async fn with_nested_reader_async<V: AsyncVisitor>(
320 &self,
321 parent: &Reader<'_>,
322 origin: Option<&SourceMap>,
323 encoding: Encoding,
324 field: &'static str,
325 visitor: &mut V,
326 ) -> core::result::Result<V::Output, ContentError<V::Error>> {
327 let prepared = self
328 .prepare_nested(parent, field, origin)
329 .map_err(ContentError::Processing)?;
330 let reader = self.parser().read_nested_query_in(
331 parent,
332 prepared.bytes(),
333 prepared.base,
334 encoding,
335 self.outer_depth(),
336 );
337 let mut reader = match reader {
338 Ok(reader) => reader,
339 Err(cause) => {
340 return Err(ContentError::Decoding {
341 source: Error::encoding(cause, field),
342 context: prepared.context,
343 });
344 }
345 };
346 let result = visitor.visit(&mut reader, &prepared.context).await;
347 match result {
348 Ok(output) => Ok(output),
349 Err(source) => Err(ContentError::Callback {
350 source,
351 context: prepared.context,
352 }),
353 }
354 }
355
356 fn prepare_nested<'a>(
357 &'a self,
358 parent: &Reader<'_>,
359 field: &'static str,
360 origin: Option<&SourceMap>,
361 ) -> Result<Prepared<'a>> {
362 if !core::ptr::eq(self.parser(), parent.parser()) {
363 let at = origin.map_or(self.span().start, |source| source.carrier().start);
364 return Err(Error::pkix(structured_pkix::Error::new(
365 structured_pkix::ErrorCode::ForeignReader,
366 field,
367 at,
368 )));
369 }
370 let context = match origin {
371 Some(origin) => SourceMap::mapped(self, parent, field, origin),
372 None => SourceMap::of(self, parent, field),
373 }?;
374 let (carried, base) = if let [piece] = self.pieces() {
375 let base = if context.coordinates() == Coordinates::Original {
376 piece.span().start
377 } else {
378 0
379 };
380 (Carried::Borrowed(piece.as_bytes()), base)
381 } else {
382 (
383 Carried::Joined(self.copy_in(parent, field, context.carrier().start)?),
384 0,
385 )
386 };
387 Ok(Prepared {
388 carried,
389 context,
390 base,
391 })
392 }
393 #[expect(
403 clippy::result_large_err,
404 reason = "the failure owns both the original cause and its source map; boxing would require another admitted allocation on the refusal path"
405 )]
406 pub fn with_nested_reader<R, E>(
407 &self,
408 parent: &Reader<'_>,
409 encoding: Encoding,
410 field: &'static str,
411 callback: impl for<'scope> FnOnce(&mut Reader<'scope>, &SourceMap) -> core::result::Result<R, E>,
412 ) -> core::result::Result<R, ContentError<E>> {
413 self.nested(parent, encoding, field, None, callback)
414 }
415
416 #[expect(
424 clippy::result_large_err,
425 reason = "retain original cause and source context without another allocation on refusal"
426 )]
427 pub fn with_nested_reader_mapped<R, E>(
428 &self,
429 parent: &Reader<'_>,
430 source: &SourceMap,
431 encoding: Encoding,
432 field: &'static str,
433 callback: impl for<'scope> FnOnce(&mut Reader<'scope>, &SourceMap) -> core::result::Result<R, E>,
434 ) -> core::result::Result<R, ContentError<E>> {
435 self.nested(parent, encoding, field, Some(source), callback)
436 }
437
438 #[expect(
439 clippy::result_large_err,
440 reason = "the owned source context must accompany its unchanged original cause"
441 )]
442 fn nested<R, E>(
443 &self,
444 parent: &Reader<'_>,
445 encoding: Encoding,
446 field: &'static str,
447 origin: Option<&SourceMap>,
448 callback: impl for<'scope> FnOnce(&mut Reader<'scope>, &SourceMap) -> core::result::Result<R, E>,
449 ) -> core::result::Result<R, ContentError<E>> {
450 let prepared = self
451 .prepare_nested(parent, field, origin)
452 .map_err(ContentError::Processing)?;
453 let reader = self.parser().read_nested_query_in(
454 parent,
455 prepared.bytes(),
456 prepared.base,
457 encoding,
458 self.outer_depth(),
459 );
460 let mut reader = match reader {
461 Ok(reader) => reader,
462 Err(source) => {
463 return Err(ContentError::Decoding {
464 source: Error::encoding(source, field),
465 context: prepared.context,
466 });
467 }
468 };
469 match callback(&mut reader, &prepared.context) {
470 Ok(output) => Ok(output),
471 Err(source) => Err(ContentError::Callback {
472 source,
473 context: prepared.context,
474 }),
475 }
476 }
477}