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 loop {
203 if r.read_eol().is_some() {
208 result.push(b'\n');
209 continue;
210 }
211
212 let Some(byte) = r.read_byte() else {
213 break;
214 };
215
216 match byte {
217 b'\\' => {
218 if r.read_eol().is_some() {
224 continue;
225 }
226
227 let next = r.read_byte()?;
228
229 if is_octal_digit(next) {
230 let second = r.read_byte();
231 let third = r.read_byte();
232
233 let bytes = match (second, third) {
234 (Some(n1), Some(n2)) => match (is_octal_digit(n1), is_octal_digit(n2)) {
235 (true, true) => [next, n1, n2],
236 (true, _) => {
237 r.jump(r.offset() - 1);
238 [b'0', next, n1]
239 }
240 _ => {
241 r.jump(r.offset() - 2);
242 [b'0', b'0', next]
243 }
244 },
245 (Some(n1), None) => {
246 if is_octal_digit(n1) {
247 [b'0', next, n1]
248 } else {
249 r.jump(r.offset() - 1);
250 [b'0', b'0', next]
251 }
252 }
253 _ => [b'0', b'0', next],
254 };
255
256 let str = core::str::from_utf8(&bytes).unwrap();
257
258 if let Ok(num) = u8::from_str_radix(str, 8) {
259 result.push(num);
260 }
261 } else {
262 match next {
263 b'n' => result.push(0xA),
264 b'r' => result.push(0xD),
265 b't' => result.push(0x9),
266 b'b' => result.push(0x8),
267 b'f' => result.push(0xC),
268 b'(' => result.push(b'('),
269 b')' => result.push(b')'),
270 b'\\' => result.push(b'\\'),
271 _ => result.push(next),
272 }
273 }
274 }
275 b'(' | b')' => result.push(byte),
276 other => result.push(other),
277 }
278 }
279
280 Some(StringInner::Owned(result))
281}
282
283fn is_octal_digit(byte: u8) -> bool {
284 matches!(byte, b'0'..=b'7')
285}
286
287#[cfg(test)]
288mod tests {
289 use crate::object::String;
290 use crate::reader::Reader;
291 use crate::reader::ReaderExt;
292
293 #[test]
294 fn display() {
295 let normal = Reader::new(b"(Hello)")
296 .read_without_context::<String<'_>>()
297 .unwrap();
298 let binary = Reader::new(b"<00FF>")
299 .read_without_context::<String<'_>>()
300 .unwrap();
301
302 assert_eq!(format!("{normal}"), "\"Hello\"");
303 assert_eq!(format!("{binary}"), "[0, 255]");
304 }
305
306 #[test]
307 fn hex_string_empty() {
308 assert_eq!(
309 Reader::new(b"<>")
310 .read_without_context::<String<'_>>()
311 .unwrap()
312 .as_bytes(),
313 b""
314 );
315 }
316
317 #[test]
318 fn hex_string_1() {
319 assert_eq!(
320 Reader::new(b"<00010203>")
321 .read_without_context::<String<'_>>()
322 .unwrap()
323 .as_bytes(),
324 &[0x00, 0x01, 0x02, 0x03]
325 );
326 }
327
328 #[test]
329 fn hex_string_2() {
330 assert_eq!(
331 Reader::new(b"<000102034>")
332 .read_without_context::<String<'_>>()
333 .unwrap()
334 .as_bytes(),
335 &[0x00, 0x01, 0x02, 0x03, 0x40]
336 );
337 }
338
339 #[test]
340 fn hex_string_trailing_1() {
341 assert_eq!(
342 Reader::new(b"<000102034>dfgfg4")
343 .read_without_context::<String<'_>>()
344 .unwrap()
345 .as_bytes(),
346 &[0x00, 0x01, 0x02, 0x03, 0x40]
347 );
348 }
349
350 #[test]
351 fn hex_string_trailing_2() {
352 assert_eq!(
353 Reader::new(b"<1 3 4>dfgfg4")
354 .read_without_context::<String<'_>>()
355 .unwrap()
356 .as_bytes(),
357 &[0x13, 0x40]
358 );
359 }
360
361 #[test]
362 fn hex_string_trailing_3() {
363 assert_eq!(
364 Reader::new(b"<1>dfgfg4")
365 .read_without_context::<String<'_>>()
366 .unwrap()
367 .as_bytes(),
368 &[0x10]
369 );
370 }
371
372 #[test]
373 fn hex_string_invalid_1() {
374 assert!(
375 Reader::new(b"<")
376 .read_without_context::<String<'_>>()
377 .is_none()
378 );
379 }
380
381 #[test]
382 fn hex_string_invalid_2() {
383 assert!(
384 Reader::new(b"34AD")
385 .read_without_context::<String<'_>>()
386 .is_none()
387 );
388 }
389
390 #[test]
391 fn literal_string_empty() {
392 assert_eq!(
393 Reader::new(b"()")
394 .read_without_context::<String<'_>>()
395 .unwrap()
396 .as_bytes(),
397 b""
398 );
399 }
400
401 #[test]
402 fn literal_string_1() {
403 assert_eq!(
404 Reader::new(b"(Hi there.)")
405 .read_without_context::<String<'_>>()
406 .unwrap()
407 .as_bytes(),
408 b"Hi there."
409 );
410 }
411
412 #[test]
413 fn literal_string_2() {
414 assert!(
415 Reader::new(b"(Hi \\777)")
416 .read_without_context::<String<'_>>()
417 .is_some()
418 );
419 }
420
421 #[test]
422 fn literal_string_3() {
423 assert_eq!(
424 Reader::new(b"(Hi ) there.)")
425 .read_without_context::<String<'_>>()
426 .unwrap()
427 .as_bytes(),
428 b"Hi "
429 );
430 }
431
432 #[test]
433 fn literal_string_4() {
434 assert_eq!(
435 Reader::new(b"(Hi (()) there)")
436 .read_without_context::<String<'_>>()
437 .unwrap()
438 .as_bytes(),
439 b"Hi (()) there"
440 );
441 }
442
443 #[test]
444 fn literal_string_5() {
445 assert_eq!(
446 Reader::new(b"(Hi \\()")
447 .read_without_context::<String<'_>>()
448 .unwrap()
449 .as_bytes(),
450 b"Hi ("
451 );
452 }
453
454 #[test]
455 fn literal_string_6() {
456 assert_eq!(
457 Reader::new(b"(Hi \\\nthere)")
458 .read_without_context::<String<'_>>()
459 .unwrap()
460 .as_bytes(),
461 b"Hi there"
462 );
463 }
464
465 #[test]
466 fn literal_string_7() {
467 assert_eq!(
468 Reader::new(b"(Hi \\05354)")
469 .read_without_context::<String<'_>>()
470 .unwrap()
471 .as_bytes(),
472 b"Hi +54"
473 );
474 }
475
476 #[test]
477 fn literal_string_8() {
478 assert_eq!(
479 Reader::new(b"(\\3)")
480 .read_without_context::<String<'_>>()
481 .unwrap()
482 .as_bytes(),
483 b"\x03"
484 );
485 }
486
487 #[test]
488 fn literal_string_9() {
489 assert_eq!(
490 Reader::new(b"(\\36)")
491 .read_without_context::<String<'_>>()
492 .unwrap()
493 .as_bytes(),
494 b"\x1e"
495 );
496 }
497
498 #[test]
499 fn literal_string_10() {
500 assert_eq!(
501 Reader::new(b"(\\36ab)")
502 .read_without_context::<String<'_>>()
503 .unwrap()
504 .as_bytes(),
505 b"\x1eab"
506 );
507 }
508
509 #[test]
510 fn literal_string_11() {
511 assert_eq!(
512 Reader::new(b"(\\00Y)")
513 .read_without_context::<String<'_>>()
514 .unwrap()
515 .as_bytes(),
516 b"\0Y"
517 );
518 }
519
520 #[test]
521 fn literal_string_12() {
522 assert_eq!(
523 Reader::new(b"(\\0Y)")
524 .read_without_context::<String<'_>>()
525 .unwrap()
526 .as_bytes(),
527 b"\0Y"
528 );
529 }
530
531 #[test]
532 fn literal_string_eol() {
533 let read = |data: &[u8]| {
534 Reader::new(data)
535 .read_without_context::<String<'_>>()
536 .unwrap()
537 .as_bytes()
538 .to_vec()
539 };
540
541 assert_eq!(read(b"(a\n\nb)"), b"a\n\nb");
542 assert_eq!(read(b"(a\r\n\r\nb)"), b"a\n\nb");
543 assert_eq!(read(b"(a\r\rb)"), b"a\n\nb");
544 assert_eq!(read(b"(a\\\n\nb)"), b"a\nb");
545 assert_eq!(read(b"(a\\\r\n\r\nb)"), b"a\nb");
546 }
547
548 #[test]
549 fn literal_string_trailing() {
550 assert_eq!(
551 Reader::new(b"(Hi there.)abcde")
552 .read_without_context::<String<'_>>()
553 .unwrap()
554 .as_bytes(),
555 b"Hi there."
556 );
557 }
558
559 #[test]
560 fn literal_string_invalid() {
561 assert_eq!(
562 Reader::new(b"(Hi \\778)")
563 .read_without_context::<String<'_>>()
564 .unwrap()
565 .as_bytes(),
566 b"Hi \x3F8"
567 );
568 }
569
570 #[test]
571 fn string_1() {
572 assert_eq!(
573 Reader::new(b"(Hi there.)")
574 .read_without_context::<String<'_>>()
575 .unwrap()
576 .as_bytes(),
577 b"Hi there."
578 );
579 }
580
581 #[test]
582 fn string_2() {
583 assert_eq!(
584 Reader::new(b"<00010203>")
585 .read_without_context::<String<'_>>()
586 .unwrap()
587 .as_bytes(),
588 &[0x00, 0x01, 0x02, 0x03]
589 );
590 }
591}