1use crate::crypto::DecryptionTarget;
4use crate::filter::ascii_hex;
5use crate::object::Object;
6use crate::object::macros::object;
7use crate::reader::Reader;
8use crate::reader::{Readable, ReaderContext, ReaderExt, Skippable};
9use crate::trivia::is_white_space_character;
10use alloc::vec::Vec;
11use core::borrow::Borrow;
12use core::hash::{Hash, Hasher};
13use core::ops::Deref;
14use smallvec::SmallVec;
15
16#[derive(Clone)]
17enum StringInner<'a> {
18 Borrowed(&'a [u8]),
19 Owned(SmallVec<[u8; 23]>),
20}
21
22impl AsRef<[u8]> for StringInner<'_> {
23 fn as_ref(&self) -> &[u8] {
24 match self {
25 Self::Borrowed(data) => data,
26 Self::Owned(data) => data,
27 }
28 }
29}
30
31#[derive(Clone)]
33pub struct String<'a>(StringInner<'a>);
34
35impl<'a> String<'a> {
36 pub fn as_bytes(&self) -> &[u8] {
38 self.as_ref()
39 }
40}
41
42impl Deref for String<'_> {
43 type Target = [u8];
44
45 fn deref(&self) -> &Self::Target {
46 self.as_ref()
47 }
48}
49
50impl AsRef<[u8]> for String<'_> {
51 fn as_ref(&self) -> &[u8] {
52 match &self.0 {
53 StringInner::Borrowed(data) => data,
54 StringInner::Owned(data) => data,
55 }
56 }
57}
58
59impl Borrow<[u8]> for String<'_> {
60 fn borrow(&self) -> &[u8] {
61 self.as_ref()
62 }
63}
64
65impl PartialEq for String<'_> {
66 fn eq(&self, other: &Self) -> bool {
67 self.as_ref() == other.as_ref()
68 }
69}
70
71impl Eq for String<'_> {}
72
73impl Hash for String<'_> {
74 fn hash<H: Hasher>(&self, state: &mut H) {
75 self.as_ref().hash(state);
76 }
77}
78
79impl core::fmt::Debug for String<'_> {
80 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81 <[u8] as core::fmt::Debug>::fmt(self.as_ref(), f)
82 }
83}
84
85impl core::fmt::Display for String<'_> {
86 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 match core::str::from_utf8(self.as_bytes()) {
88 Ok(value) => write!(f, "{value:?}"),
89 Err(_) => write!(f, "{:?}", self.as_bytes()),
90 }
91 }
92}
93
94object!(String<'a>, String);
95
96impl Skippable for String<'_> {
97 fn skip(r: &mut Reader<'_>, _: bool) -> Option<()> {
98 match r.peek_byte()? {
99 b'<' => skip_hex(r),
100 b'(' => skip_literal(r),
101 _ => None,
102 }
103 }
104}
105
106impl<'a> Readable<'a> for String<'a> {
107 fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
108 let decoded = match r.peek_byte()? {
109 b'<' => StringInner::Owned(read_hex(r)?),
110 b'(' => read_literal(r)?,
111 _ => return None,
112 };
113
114 let final_data = if ctx.xref().needs_decryption(ctx) {
116 if let Some(obj_number) = ctx.obj_number() {
117 ctx.xref()
118 .decrypt(obj_number, decoded.as_ref(), DecryptionTarget::String)
119 .map(StringInner::from)
120 .unwrap_or(decoded)
121 } else {
122 decoded
123 }
124 } else {
125 decoded
126 };
127
128 Some(Self(final_data))
129 }
130}
131
132impl From<Vec<u8>> for StringInner<'_> {
133 fn from(value: Vec<u8>) -> Self {
134 Self::Owned(SmallVec::from_vec(value))
135 }
136}
137
138fn skip_hex(r: &mut Reader<'_>) -> Option<()> {
139 r.forward_tag(b"<")?;
140 while let Some(b) = r.peek_byte() {
141 let is_hex = b.is_ascii_hexdigit();
142 let is_whitespace = is_white_space_character(b);
143
144 if !is_hex && !is_whitespace {
145 break;
146 }
147
148 r.read_byte()?;
149 }
150 r.forward_tag(b">")?;
151
152 Some(())
153}
154
155fn read_hex(r: &mut Reader<'_>) -> Option<SmallVec<[u8; 23]>> {
156 let start = r.offset();
157 skip_hex(r)?;
158 let end = r.offset();
159
160 let raw = r.range(start + 1..end - 1)?;
162 let decoded = ascii_hex::decode_into(raw)?;
163
164 Some(decoded)
165}
166
167fn skip_literal(r: &mut Reader<'_>) -> Option<()> {
168 r.forward_tag(b"(")?;
169 let mut bracket_counter = 1;
170
171 while bracket_counter > 0 {
172 let byte = r.read_byte()?;
173
174 match byte {
175 b'\\' => {
176 let _ = r.read_byte()?;
177 }
178 b'(' => bracket_counter += 1,
179 b')' => bracket_counter -= 1,
180 _ => {}
181 };
182 }
183
184 Some(())
185}
186
187fn read_literal<'a>(r: &mut Reader<'a>) -> Option<StringInner<'a>> {
188 let start = r.offset();
189 skip_literal(r)?;
190 let end = r.offset();
191
192 let data = r.range(start + 1..end - 1)?;
194
195 if !data.iter().any(|b| matches!(b, b'\\' | b'\n' | b'\r')) {
196 return Some(StringInner::Borrowed(data));
197 }
198
199 let mut r = Reader::new(data);
200 let mut result = SmallVec::new();
201
202 while let Some(byte) = r.read_byte() {
203 match byte {
204 b'\\' => {
205 let next = r.read_byte()?;
206
207 if is_octal_digit(next) {
208 let second = r.read_byte();
209 let third = r.read_byte();
210
211 let bytes = match (second, third) {
212 (Some(n1), Some(n2)) => match (is_octal_digit(n1), is_octal_digit(n2)) {
213 (true, true) => [next, n1, n2],
214 (true, _) => {
215 r.jump(r.offset() - 1);
216 [b'0', next, n1]
217 }
218 _ => {
219 r.jump(r.offset() - 2);
220 [b'0', b'0', next]
221 }
222 },
223 (Some(n1), None) => {
224 if is_octal_digit(n1) {
225 [b'0', next, n1]
226 } else {
227 r.jump(r.offset() - 1);
228 [b'0', b'0', next]
229 }
230 }
231 _ => [b'0', b'0', next],
232 };
233
234 let str = core::str::from_utf8(&bytes).unwrap();
235
236 if let Ok(num) = u8::from_str_radix(str, 8) {
237 result.push(num);
238 } else {
239 warn!("overflow occurred while parsing octal literal string");
240 }
241 } else {
242 match next {
243 b'n' => result.push(0xA),
244 b'r' => result.push(0xD),
245 b't' => result.push(0x9),
246 b'b' => result.push(0x8),
247 b'f' => result.push(0xC),
248 b'(' => result.push(b'('),
249 b')' => result.push(b')'),
250 b'\\' => result.push(b'\\'),
251 b'\n' | b'\r' => {
252 r.skip_eol_characters();
258 }
259 _ => result.push(next),
260 }
261 }
262 }
263 b'(' | b')' => result.push(byte),
264 b'\n' | b'\r' => {
269 result.push(b'\n');
270 r.skip_eol_characters();
271 }
272 other => result.push(other),
273 }
274 }
275
276 Some(StringInner::Owned(result))
277}
278
279fn is_octal_digit(byte: u8) -> bool {
280 matches!(byte, b'0'..=b'7')
281}
282
283#[cfg(test)]
284mod tests {
285 use crate::object::String;
286 use crate::reader::Reader;
287 use crate::reader::ReaderExt;
288
289 #[test]
290 fn display() {
291 let normal = Reader::new(b"(Hello)")
292 .read_without_context::<String<'_>>()
293 .unwrap();
294 let binary = Reader::new(b"<00FF>")
295 .read_without_context::<String<'_>>()
296 .unwrap();
297
298 assert_eq!(format!("{normal}"), "\"Hello\"");
299 assert_eq!(format!("{binary}"), "[0, 255]");
300 }
301
302 #[test]
303 fn hex_string_empty() {
304 assert_eq!(
305 Reader::new(b"<>")
306 .read_without_context::<String<'_>>()
307 .unwrap()
308 .as_bytes(),
309 b""
310 );
311 }
312
313 #[test]
314 fn hex_string_1() {
315 assert_eq!(
316 Reader::new(b"<00010203>")
317 .read_without_context::<String<'_>>()
318 .unwrap()
319 .as_bytes(),
320 &[0x00, 0x01, 0x02, 0x03]
321 );
322 }
323
324 #[test]
325 fn hex_string_2() {
326 assert_eq!(
327 Reader::new(b"<000102034>")
328 .read_without_context::<String<'_>>()
329 .unwrap()
330 .as_bytes(),
331 &[0x00, 0x01, 0x02, 0x03, 0x40]
332 );
333 }
334
335 #[test]
336 fn hex_string_trailing_1() {
337 assert_eq!(
338 Reader::new(b"<000102034>dfgfg4")
339 .read_without_context::<String<'_>>()
340 .unwrap()
341 .as_bytes(),
342 &[0x00, 0x01, 0x02, 0x03, 0x40]
343 );
344 }
345
346 #[test]
347 fn hex_string_trailing_2() {
348 assert_eq!(
349 Reader::new(b"<1 3 4>dfgfg4")
350 .read_without_context::<String<'_>>()
351 .unwrap()
352 .as_bytes(),
353 &[0x13, 0x40]
354 );
355 }
356
357 #[test]
358 fn hex_string_trailing_3() {
359 assert_eq!(
360 Reader::new(b"<1>dfgfg4")
361 .read_without_context::<String<'_>>()
362 .unwrap()
363 .as_bytes(),
364 &[0x10]
365 );
366 }
367
368 #[test]
369 fn hex_string_invalid_1() {
370 assert!(
371 Reader::new(b"<")
372 .read_without_context::<String<'_>>()
373 .is_none()
374 );
375 }
376
377 #[test]
378 fn hex_string_invalid_2() {
379 assert!(
380 Reader::new(b"34AD")
381 .read_without_context::<String<'_>>()
382 .is_none()
383 );
384 }
385
386 #[test]
387 fn literal_string_empty() {
388 assert_eq!(
389 Reader::new(b"()")
390 .read_without_context::<String<'_>>()
391 .unwrap()
392 .as_bytes(),
393 b""
394 );
395 }
396
397 #[test]
398 fn literal_string_1() {
399 assert_eq!(
400 Reader::new(b"(Hi there.)")
401 .read_without_context::<String<'_>>()
402 .unwrap()
403 .as_bytes(),
404 b"Hi there."
405 );
406 }
407
408 #[test]
409 fn literal_string_2() {
410 assert!(
411 Reader::new(b"(Hi \\777)")
412 .read_without_context::<String<'_>>()
413 .is_some()
414 );
415 }
416
417 #[test]
418 fn literal_string_3() {
419 assert_eq!(
420 Reader::new(b"(Hi ) there.)")
421 .read_without_context::<String<'_>>()
422 .unwrap()
423 .as_bytes(),
424 b"Hi "
425 );
426 }
427
428 #[test]
429 fn literal_string_4() {
430 assert_eq!(
431 Reader::new(b"(Hi (()) there)")
432 .read_without_context::<String<'_>>()
433 .unwrap()
434 .as_bytes(),
435 b"Hi (()) there"
436 );
437 }
438
439 #[test]
440 fn literal_string_5() {
441 assert_eq!(
442 Reader::new(b"(Hi \\()")
443 .read_without_context::<String<'_>>()
444 .unwrap()
445 .as_bytes(),
446 b"Hi ("
447 );
448 }
449
450 #[test]
451 fn literal_string_6() {
452 assert_eq!(
453 Reader::new(b"(Hi \\\nthere)")
454 .read_without_context::<String<'_>>()
455 .unwrap()
456 .as_bytes(),
457 b"Hi there"
458 );
459 }
460
461 #[test]
462 fn literal_string_7() {
463 assert_eq!(
464 Reader::new(b"(Hi \\05354)")
465 .read_without_context::<String<'_>>()
466 .unwrap()
467 .as_bytes(),
468 b"Hi +54"
469 );
470 }
471
472 #[test]
473 fn literal_string_8() {
474 assert_eq!(
475 Reader::new(b"(\\3)")
476 .read_without_context::<String<'_>>()
477 .unwrap()
478 .as_bytes(),
479 b"\x03"
480 );
481 }
482
483 #[test]
484 fn literal_string_9() {
485 assert_eq!(
486 Reader::new(b"(\\36)")
487 .read_without_context::<String<'_>>()
488 .unwrap()
489 .as_bytes(),
490 b"\x1e"
491 );
492 }
493
494 #[test]
495 fn literal_string_10() {
496 assert_eq!(
497 Reader::new(b"(\\36ab)")
498 .read_without_context::<String<'_>>()
499 .unwrap()
500 .as_bytes(),
501 b"\x1eab"
502 );
503 }
504
505 #[test]
506 fn literal_string_11() {
507 assert_eq!(
508 Reader::new(b"(\\00Y)")
509 .read_without_context::<String<'_>>()
510 .unwrap()
511 .as_bytes(),
512 b"\0Y"
513 );
514 }
515
516 #[test]
517 fn literal_string_12() {
518 assert_eq!(
519 Reader::new(b"(\\0Y)")
520 .read_without_context::<String<'_>>()
521 .unwrap()
522 .as_bytes(),
523 b"\0Y"
524 );
525 }
526
527 #[test]
528 fn literal_string_trailing() {
529 assert_eq!(
530 Reader::new(b"(Hi there.)abcde")
531 .read_without_context::<String<'_>>()
532 .unwrap()
533 .as_bytes(),
534 b"Hi there."
535 );
536 }
537
538 #[test]
539 fn literal_string_invalid() {
540 assert_eq!(
541 Reader::new(b"(Hi \\778)")
542 .read_without_context::<String<'_>>()
543 .unwrap()
544 .as_bytes(),
545 b"Hi \x3F8"
546 );
547 }
548
549 #[test]
550 fn string_1() {
551 assert_eq!(
552 Reader::new(b"(Hi there.)")
553 .read_without_context::<String<'_>>()
554 .unwrap()
555 .as_bytes(),
556 b"Hi there."
557 );
558 }
559
560 #[test]
561 fn string_2() {
562 assert_eq!(
563 Reader::new(b"<00010203>")
564 .read_without_context::<String<'_>>()
565 .unwrap()
566 .as_bytes(),
567 &[0x00, 0x01, 0x02, 0x03]
568 );
569 }
570}