1use std::borrow::Borrow;
5
6use crate::hash::FastMap;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct ObjRef {
11 pub num: u32,
12 pub gen: u16,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub struct Name(pub String);
19
20impl Borrow<str> for Name {
21 fn borrow(&self) -> &str {
22 &self.0
23 }
24}
25
26#[derive(Debug, Clone, Default, PartialEq)]
28pub struct Dict(FastMap<Name, Object>);
29
30impl Dict {
31 pub fn new() -> Dict {
33 Dict::default()
34 }
35
36 pub fn get(&self, key: &str) -> Option<&Object> {
38 self.0.get(key)
39 }
40
41 pub fn insert(&mut self, key: Name, value: Object) -> Option<Object> {
43 self.0.insert(key, value)
44 }
45
46 pub fn remove(&mut self, key: &str) -> Option<Object> {
48 self.0.remove(key)
49 }
50
51 pub fn iter(&self) -> impl Iterator<Item = (&Name, &Object)> {
53 self.0.iter()
54 }
55
56 pub(crate) fn values_mut(&mut self) -> impl Iterator<Item = &mut Object> {
58 self.0.values_mut()
59 }
60
61 pub fn len(&self) -> usize {
63 self.0.len()
64 }
65
66 pub fn is_empty(&self) -> bool {
68 self.0.is_empty()
69 }
70
71 pub fn get_int(&self, key: &str) -> Option<i64> {
73 self.get(key)?.as_int()
74 }
75
76 pub fn get_f64(&self, key: &str) -> Option<f64> {
78 self.get(key)?.as_f64()
79 }
80
81 pub fn get_name(&self, key: &str) -> Option<&Name> {
83 self.get(key)?.as_name()
84 }
85
86 pub fn get_array(&self, key: &str) -> Option<&[Object]> {
88 self.get(key)?.as_array()
89 }
90
91 pub fn get_dict(&self, key: &str) -> Option<&Dict> {
93 self.get(key)?.as_dict()
94 }
95
96 pub fn get_ref(&self, key: &str) -> Option<ObjRef> {
98 self.get(key)?.as_ref()
99 }
100}
101
102#[derive(Debug, Clone, PartialEq)]
104pub struct Stream {
105 pub dict: Dict,
106 pub data: Vec<u8>,
108}
109
110#[derive(Debug, Clone, PartialEq)]
112pub enum Object {
113 Null,
114 Bool(bool),
115 Int(i64),
116 Real(f64),
117 String(Vec<u8>),
119 Name(Name),
120 Array(Vec<Object>),
121 Dict(Dict),
122 Stream(Stream),
123 Ref(ObjRef),
124}
125
126impl Object {
127 pub fn as_bool(&self) -> Option<bool> {
129 match self {
130 Object::Bool(b) => Some(*b),
131 _ => None,
132 }
133 }
134
135 pub fn as_int(&self) -> Option<i64> {
137 match self {
138 Object::Int(i) => Some(*i),
139 _ => None,
140 }
141 }
142
143 pub fn as_f64(&self) -> Option<f64> {
145 match self {
146 Object::Int(i) => Some(*i as f64),
147 Object::Real(r) => Some(*r),
148 _ => None,
149 }
150 }
151
152 pub fn as_str_bytes(&self) -> Option<&[u8]> {
154 match self {
155 Object::String(bytes) => Some(bytes),
156 _ => None,
157 }
158 }
159
160 pub fn as_name(&self) -> Option<&Name> {
162 match self {
163 Object::Name(n) => Some(n),
164 _ => None,
165 }
166 }
167
168 pub fn as_array(&self) -> Option<&[Object]> {
170 match self {
171 Object::Array(items) => Some(items),
172 _ => None,
173 }
174 }
175
176 pub fn as_dict(&self) -> Option<&Dict> {
178 match self {
179 Object::Dict(d) => Some(d),
180 Object::Stream(s) => Some(&s.dict),
181 _ => None,
182 }
183 }
184
185 pub fn as_stream(&self) -> Option<&Stream> {
187 match self {
188 Object::Stream(s) => Some(s),
189 _ => None,
190 }
191 }
192
193 #[allow(clippy::should_implement_trait)] pub fn as_ref(&self) -> Option<ObjRef> {
196 match self {
197 Object::Ref(r) => Some(*r),
198 _ => None,
199 }
200 }
201
202 pub fn is_null(&self) -> bool {
204 matches!(self, Object::Null)
205 }
206}
207
208pub fn decode_text_string(bytes: &[u8]) -> String {
212 if let Some(rest) = bytes.strip_prefix(&[0xFE, 0xFF]) {
213 let units = rest
214 .as_chunks::<2>()
215 .0
216 .iter()
217 .map(|pair| u16::from_be_bytes([pair[0], pair[1]]));
218 let mut out: String = std::char::decode_utf16(units)
219 .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
220 .collect();
221 if rest.len() % 2 == 1 {
222 out.push(char::REPLACEMENT_CHARACTER);
224 }
225 out
226 } else if let Some(rest) = bytes.strip_prefix(&[0xEF, 0xBB, 0xBF]) {
227 String::from_utf8_lossy(rest).into_owned()
228 } else {
229 bytes.iter().map(|&b| char::from(b)).collect()
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238
239 fn name(s: &str) -> Name {
240 Name(s.to_string())
241 }
242
243 #[test]
244 fn accessors_return_some_for_matching_variant() {
245 assert_eq!(Object::Bool(true).as_bool(), Some(true));
246 assert_eq!(Object::Bool(false).as_bool(), Some(false));
247 assert_eq!(Object::Int(-7).as_int(), Some(-7));
248 assert_eq!(Object::Real(1.5).as_f64(), Some(1.5));
249 assert_eq!(
250 Object::String(b"abc".to_vec()).as_str_bytes(),
251 Some(b"abc".as_slice())
252 );
253 assert_eq!(Object::Name(name("Type")).as_name(), Some(&name("Type")));
254 let arr = Object::Array(vec![Object::Int(1), Object::Null]);
255 assert_eq!(arr.as_array().map(<[Object]>::len), Some(2));
256 let mut d = Dict::new();
257 d.insert(name("K"), Object::Int(9));
258 assert_eq!(Object::Dict(d.clone()).as_dict(), Some(&d));
259 let r = ObjRef { num: 12, gen: 3 };
260 assert_eq!(Object::Ref(r).as_ref(), Some(r));
261 assert!(Object::Null.is_null());
262 assert!(!Object::Int(0).is_null());
263 }
264
265 #[test]
266 fn accessors_return_none_for_other_variants() {
267 let o = Object::Int(1);
268 assert_eq!(o.as_bool(), None);
269 assert_eq!(o.as_str_bytes(), None);
270 assert_eq!(o.as_name(), None);
271 assert_eq!(o.as_array(), None);
272 assert_eq!(o.as_dict(), None);
273 assert_eq!(o.as_stream(), None);
274 assert_eq!(o.as_ref(), None);
275 assert_eq!(Object::Real(2.0).as_int(), None);
276 assert_eq!(Object::Bool(true).as_f64(), None);
277 assert_eq!(Object::Null.as_f64(), None);
278 }
279
280 #[test]
281 fn as_f64_coerces_int() {
282 assert_eq!(Object::Int(42).as_f64(), Some(42.0));
283 assert_eq!(Object::Int(-3).as_f64(), Some(-3.0));
284 assert_eq!(Object::Real(0.25).as_f64(), Some(0.25));
285 }
286
287 #[test]
288 fn as_dict_sees_stream_dict() {
289 let mut d = Dict::new();
290 d.insert(name("Length"), Object::Int(5));
291 let s = Stream {
292 dict: d.clone(),
293 data: b"hello".to_vec(),
294 };
295 let o = Object::Stream(s.clone());
296 assert_eq!(o.as_dict(), Some(&d));
297 assert_eq!(o.as_stream(), Some(&s));
298 }
299
300 #[test]
301 fn dict_basic_operations() {
302 let mut d = Dict::new();
303 assert!(d.is_empty());
304 assert_eq!(d.len(), 0);
305 assert_eq!(d.insert(name("A"), Object::Int(1)), None);
306 assert_eq!(
307 d.insert(name("A"), Object::Int(2)),
308 Some(Object::Int(1)),
309 "insert returns the previous value"
310 );
311 d.insert(name("B"), Object::Bool(true));
312 assert_eq!(d.len(), 2);
313 assert!(!d.is_empty());
314 assert_eq!(d.get("A"), Some(&Object::Int(2)));
315 assert_eq!(d.get("Missing"), None);
316 let mut keys: Vec<&str> = d.iter().map(|(k, _)| k.0.as_str()).collect();
317 keys.sort_unstable();
318 assert_eq!(keys, vec!["A", "B"]);
319 assert_eq!(d.remove("A"), Some(Object::Int(2)));
320 assert_eq!(d.remove("A"), None);
321 assert_eq!(d.len(), 1);
322 }
323
324 #[test]
325 fn dict_typed_helpers() {
326 let mut inner = Dict::new();
327 inner.insert(name("X"), Object::Null);
328 let mut d = Dict::new();
329 d.insert(name("Int"), Object::Int(7));
330 d.insert(name("Real"), Object::Real(2.5));
331 d.insert(name("Name"), Object::Name(name("Page")));
332 d.insert(name("Arr"), Object::Array(vec![Object::Int(1)]));
333 d.insert(name("Dict"), Object::Dict(inner.clone()));
334 d.insert(name("Ref"), Object::Ref(ObjRef { num: 4, gen: 0 }));
335
336 assert_eq!(d.get_int("Int"), Some(7));
337 assert_eq!(d.get_int("Real"), None, "reals do not coerce to int");
338 assert_eq!(d.get_f64("Int"), Some(7.0), "ints coerce to f64");
339 assert_eq!(d.get_f64("Real"), Some(2.5));
340 assert_eq!(d.get_name("Name"), Some(&name("Page")));
341 assert_eq!(d.get_array("Arr"), Some([Object::Int(1)].as_slice()));
342 assert_eq!(d.get_dict("Dict"), Some(&inner));
343 assert_eq!(d.get_ref("Ref"), Some(ObjRef { num: 4, gen: 0 }));
344
345 assert_eq!(d.get_int("Nope"), None);
347 assert_eq!(d.get_name("Int"), None);
348 assert_eq!(d.get_array("Dict"), None);
349 assert_eq!(d.get_dict("Arr"), None);
350 assert_eq!(d.get_ref("Int"), None);
351 }
352
353 #[test]
354 fn decode_utf16be_with_bom() {
355 assert_eq!(
356 decode_text_string(&[0xFE, 0xFF, 0x00, 0x48, 0x00, 0x69]),
357 "Hi"
358 );
359 assert_eq!(
361 decode_text_string(&[0xFE, 0xFF, 0xD8, 0x34, 0xDD, 0x1E]),
362 "\u{1D11E}"
363 );
364 assert_eq!(decode_text_string(&[0xFE, 0xFF]), "");
366 assert_eq!(decode_text_string(&[0xFE, 0xFF, 0xD8, 0x34]), "\u{FFFD}");
368 assert_eq!(
370 decode_text_string(&[0xFE, 0xFF, 0x00, 0x48, 0x12]),
371 "H\u{FFFD}"
372 );
373 }
374
375 #[test]
376 fn decode_utf8_with_bom() {
377 let mut bytes = vec![0xEF, 0xBB, 0xBF];
378 bytes.extend_from_slice("héllo €".as_bytes());
379 assert_eq!(decode_text_string(&bytes), "héllo €");
380 assert_eq!(decode_text_string(&[0xEF, 0xBB, 0xBF]), "");
381 assert_eq!(decode_text_string(&[0xEF, 0xBB, 0xBF, 0xFF]), "\u{FFFD}");
383 }
384
385 #[test]
386 fn decode_latin1_fallback() {
387 assert_eq!(decode_text_string(b"Hello"), "Hello");
388 assert_eq!(decode_text_string(&[0x48, 0xE9]), "Hé");
389 assert_eq!(decode_text_string(&[0xFF]), "ÿ");
390 assert_eq!(decode_text_string(&[]), "");
391 assert_eq!(decode_text_string(&[0xFE]), "\u{FE}");
393 }
394}