1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//! Paid PDF name/string decoding without canonicalizing signed source bytes.
use crate::byte_machine::{Decoder, Step};
use crate::error::{Error, ErrorKind, Result, Syntax};
use crate::source::Location;
use core::cmp::Ordering;
use structured_asn1::memory::{OwnedVec, Work};
/// PDF spelling of a byte value, excluding its outer delimiters.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Form {
/// Name bytes after the leading slash; valid #xx escapes denote one byte.
Name,
/// Literal string bytes between parentheses, with PDF escapes and EOL rules.
Literal,
/// Hexadecimal string bytes between angle brackets, including whitespace.
Hexadecimal,
}
/// Original encoded body and its exact source location. This is an inspection
/// input, not an assertion that its enclosing PDF object or signature is valid.
#[derive(Debug, Clone, Copy)]
pub struct Bytes<'a> {
raw: &'a [u8],
form: Form,
location: Location,
}
impl<'a> Bytes<'a> {
/// Associates an original body with an equal-length source range without
/// traversal or allocation. Syntax is checked by the paid decoding methods.
pub fn new(raw: &'a [u8], form: Form, location: Location) -> Result<Self> {
if location.span().length != raw.len() as u64 {
return Err(Error {
kind: ErrorKind::Foreign,
location,
});
}
Ok(Self {
raw,
form,
location,
})
}
/// Unmodified original body, not a decoded or re-encoded signature input.
#[must_use]
pub fn as_bytes(self) -> &'a [u8] {
self.raw
}
/// Original byte range and its coordinate domain.
#[must_use]
pub fn location(self) -> Location {
self.location
}
/// Decodes under the current work/account. Actual allocated capacity stays
/// charged for the returned owner's lifetime; refusal drops partial output.
pub fn decode_in<W: Work>(self, work: &W) -> Result<OwnedVec<u8, W>> {
let mut cursor = Cursor::new(self);
let mut out = work.vector();
while let Some(byte) = cursor.next(work)? {
out.try_push(byte).map_err(|cause| Error {
kind: ErrorKind::Resource(cause),
location: cursor.point(),
})?;
}
Ok(out)
}
/// Streaming decoded equality, without a scratch allocation or an
/// infallible `PartialEq` traversal. Each original byte is paid before read.
pub fn matches_in(self, other: Self, work: &impl Work) -> Result<bool> {
Ok(self.compare_in(other, work)? == Ordering::Equal)
}
/// Lexicographic decoded byte order, using the same streaming kernel as
/// equality. Neither case folding nor text normalization is performed.
pub fn compare_in(self, other: Self, work: &impl Work) -> Result<Ordering> {
let mut left = Cursor::new(self);
let mut right = Cursor::new(other);
loop {
match (left.next(work)?, right.next(work)?) {
(None, None) => return Ok(Ordering::Equal),
(Some(a), Some(b)) if a == b => {}
(None, Some(_)) => return Ok(Ordering::Less),
(Some(_), None) => return Ok(Ordering::Greater),
(Some(a), Some(b)) => return Ok(a.cmp(&b)),
}
}
}
/// Compares a PDF name/string with already decoded caller bytes. Caller
/// bytes are admitted before comparison as well; mismatches stop the scan.
pub fn equals_in(self, expected: &[u8], work: &impl Work) -> Result<bool> {
let mut cursor = Cursor::new(self);
let mut index = 0;
while let Some(byte) = cursor.next(work)? {
work.charge_work(1).map_err(|cause| Error {
kind: ErrorKind::Resource(cause),
location: cursor.point(),
})?;
if expected.get(index) != Some(&byte) {
return Ok(false);
}
index += 1;
}
Ok(index == expected.len())
}
}
struct Cursor<'a> {
value: Bytes<'a>,
index: usize,
decoder: Decoder,
}
impl<'a> Cursor<'a> {
fn new(value: Bytes<'a>) -> Self {
Self {
value,
index: 0,
decoder: Decoder::new(value.form),
}
}
fn point(&self) -> Location {
// Both the body range and this bounded cursor were checked at entry.
Location::new(
self.value.location.source(),
self.value.location.span().start + self.index as u64,
0,
)
.expect("a point inside the checked original body")
}
fn malformed(&self) -> Error {
Error {
kind: ErrorKind::Syntax(match self.value.form {
Form::Name => Syntax::Name,
Form::Literal | Form::Hexadecimal => Syntax::String,
}),
location: self.point(),
}
}
fn next(&mut self, work: &impl Work) -> Result<Option<u8>> {
loop {
let input = if self.index < self.value.raw.len() {
work.charge_work(1).map_err(|cause| Error {
kind: ErrorKind::Resource(cause),
location: self.point(),
})?;
Some(self.value.raw[self.index])
} else {
None
};
match self.decoder.step(input) {
Step::More => {
debug_assert!(input.is_some());
self.index += 1;
}
Step::Byte { value, consumed } => {
if consumed {
self.index += 1;
}
return Ok(Some(value));
}
Step::End => return Ok(None),
Step::Malformed => {
if input.is_some() {
self.index += 1;
}
return Err(self.malformed());
}
}
}
}
}
pub(crate) const fn is_space(byte: u8) -> bool {
matches!(byte, 0 | 9 | 10 | 12 | 13 | 32)
}