mirror of
https://github.com/storytold/craft-libs.git
synced 2026-10-13 23:17:44 +00:00
Parse duplicate PDF dictionary keys in linear time
This commit is contained in:
1 parent
85ef8b92ac
commit
c22935270b
2 files changed
+22
-5
No files matched your search
@@ -231,12 +231,16 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
|
||||
fn dict_body(&mut self, depth: usize) -> Option<Dict> {
|
||||
let mut dict: Dict = Vec::new();
|
||||
// A dictionary's last occurrence of a key wins, including its position. Scanning
|
||||
// all earlier keys for each insertion makes a small hostile dictionary quadratic.
|
||||
// Keep bounded slots while reading, then compact them once in final occurrence order.
|
||||
let mut positions = HashMap::new();
|
||||
let mut items = Vec::new();
|
||||
loop {
|
||||
self.skip_ws();
|
||||
if self.rest().starts_with(b">>") {
|
||||
self.pos += 2;
|
||||
return Some(dict);
|
||||
return Some(items.into_iter().flatten().collect());
|
||||
}
|
||||
if self.peek()? != b'/' {
|
||||
return None;
|
||||
@@ -244,9 +248,10 @@ impl<'a> Lexer<'a> {
|
||||
self.pos += 1;
|
||||
let key = self.name()?;
|
||||
let value = self.object(depth + 1)?;
|
||||
// A repeated key: the last one counts.
|
||||
dict.retain(|(k, _)| *k != key);
|
||||
dict.push((key, value));
|
||||
if let Some(previous) = positions.insert(key.clone(), items.len()) {
|
||||
*items.get_mut(previous)? = None;
|
||||
}
|
||||
items.push(Some((key, value)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -224,3 +224,15 @@ fn separate_cached_object_streams_cannot_exceed_the_job_budget() {
|
||||
pdf.extend_from_slice(format!("\nendstream\nendobj\nstartxref\n{xref}\n%%EOF\n").as_bytes());
|
||||
assert!(read(&pdf).is_err(), "three small compressed streams must not cache 36 MiB");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_dictionary_keys_keep_the_last_value_and_last_position() {
|
||||
let dict = Lexer::new(b"<< /A 1 /B 2 /A 3 /C 4 /B 5 >>", 0).object(0).unwrap();
|
||||
let dict = dict.dict().unwrap();
|
||||
assert_eq!(
|
||||
dict.iter().map(|(key, value)| (key.as_slice(), value.int().unwrap())).collect::<Vec<_>>(),
|
||||
[(b"A".as_slice(), 3), (b"C".as_slice(), 4), (b"B".as_slice(), 5)]
|
||||
);
|
||||
let escaped = Lexer::new(b"<< /A#42 1 /C 2 /AB 3 >>", 0).object(0).unwrap();
|
||||
assert_eq!(escaped.dict().unwrap(), &vec![(b"C".to_vec(), Obj::Num(b"2".to_vec())), (b"AB".to_vec(), Obj::Num(b"3".to_vec()))]);
|
||||
}
|
||||
Reference in new issue
Block a user