use std::collections::HashMap;
use crate::SOURCE_FORMAT_PDF;
use crate::container::{Descriptor, UNIVERSE};
use crate::dra::op::{DEFLATE_SOURCE_CHANNEL, DEFLATE_SOURCE_OBJECT};
use crate::dra::{Op, Program};
use crate::encode::candidates::{Candidate, CandidateKind};
use crate::error::Result;
use crate::integrity::sha256;
use crate::limits::Limits;
use crate::codec::deflate::{ReplayPlan, try_replay};
use super::cos::FilterClass;
use super::physical::{PdfPhysical, scan};
pub(super) type SpanPlans = HashMap<(u64, u64), ReplayPlan>;
pub(super) fn collect_plans(
input: &[u8],
limits: Limits,
) -> Result<Option<(PdfPhysical, SpanPlans)>> {
let physical = match scan(input, limits) {
Ok(p) => p,
Err(_) => return Ok(None),
};
if physical.header.is_none() || physical.objects.is_empty() || physical.eofs.is_empty() {
return Ok(None);
}
let mut by_span: SpanPlans = HashMap::new();
for stream in &physical.streams {
if stream.filter != FilterClass::FlateDecode {
continue;
}
if by_span.len() as u64 >= u64::from(limits.max_object_count) {
break;
}
let start = stream.data_start as usize;
let end = match start.checked_add(stream.data_len as usize) {
Some(e) if e <= input.len() => e,
_ => continue,
};
if let Some(plan) = try_replay(&input[start..end], limits) {
by_span.insert((stream.data_start, stream.data_len), plan);
}
}
if by_span.is_empty() {
return Ok(None);
}
Ok(Some((physical, by_span)))
}
pub fn propose_pdf_deflate_replay(input: &[u8], limits: Limits) -> Result<Option<Candidate>> {
let Some((physical, by_span)) = collect_plans(input, limits)? else {
return Ok(None);
};
if physical.spans.len() as u64 > u64::from(limits.max_graph_ops) {
return Ok(None);
}
let mut objects: Vec<Vec<u8>> = Vec::new();
let mut index: HashMap<Vec<u8>, u32> = HashMap::new();
let mut ops: Vec<Op> = Vec::with_capacity(physical.spans.len());
let mut replayed = 0usize;
for span in &physical.spans {
let key = (span.start, span.len);
if let Some(plan) = by_span.get(&key) {
let plaintext_id = intern(&mut objects, &mut index, plan.plaintext.clone());
let corrections_id = intern(&mut objects, &mut index, plan.corrections.clone());
if objects.len() as u64 > u64::from(limits.max_object_count) {
return Ok(None);
}
ops.push(Op::Inline {
bytes: plan.header.to_vec(),
});
ops.push(Op::DeflateReplay {
replay_codec: crate::dra::op::REPLAY_DEFLATE_PREFLATE_0_7_6,
source_kind: DEFLATE_SOURCE_OBJECT,
source_id: plaintext_id,
corrections_object: corrections_id,
declared_output_len: plan.raw_len,
});
ops.push(Op::Inline {
bytes: plan.adler.to_vec(),
});
replayed += 1;
} else {
let start = span.start as usize;
let end = start + span.len as usize;
ops.push(Op::Inline {
bytes: input[start..end].to_vec(),
});
}
if ops.len() as u64 > u64::from(limits.max_graph_ops) {
return Ok(None);
}
}
let format_basis = format!(
"pdf-deflate-replay;streams={};replayed={replayed};objects={}",
physical.streams.len(),
objects.len()
);
let descriptor = Descriptor {
universe: UNIVERSE.to_string(),
source_format: SOURCE_FORMAT_PDF,
format_basis,
models: vec![],
channels: vec![],
objects,
program: Program::new(ops),
source_sha256: sha256(input),
source_len: input.len() as u64,
};
Ok(Some(Candidate {
kind: CandidateKind::PdfDeflateReplay,
descriptor,
}))
}
#[cfg(feature = "rans")]
pub fn propose_pdf_deflate_replay_rans(input: &[u8], limits: Limits) -> Result<Option<Candidate>> {
use crate::entropy::{
CODER_ORDER0_BYTE_RANS, CODER_VERSION_1, EntropyChannelDescriptor, EntropyModel,
encode_channel,
};
let Some((physical, by_span)) = collect_plans(input, limits)? else {
return Ok(None);
};
if physical.spans.len() as u64 > u64::from(limits.max_graph_ops) {
return Ok(None);
}
let mut models: Vec<EntropyModel> = Vec::new();
let mut channels: Vec<EntropyChannelDescriptor> = Vec::new();
let mut channel_id: HashMap<Vec<u8>, u32> = HashMap::new();
let mut objects: Vec<Vec<u8>> = Vec::new();
let mut object_id: HashMap<Vec<u8>, u32> = HashMap::new();
let mut ops: Vec<Op> = Vec::with_capacity(physical.spans.len());
let mut replayed = 0usize;
for span in &physical.spans {
let key = (span.start, span.len);
if let Some(plan) = by_span.get(&key) {
let cid = if let Some(&id) = channel_id.get(&plan.plaintext) {
id
} else {
if channels.len() as u64 >= u64::from(limits.max_channel_count)
|| models.len() as u64 >= u64::from(limits.max_model_count)
{
return Ok(None);
}
let mut counts = [0u64; crate::entropy::ALPHABET];
for &b in &plan.plaintext {
counts[b as usize] += 1;
}
let model = EntropyModel::from_counts(&counts, 12)?;
let scale_bits = model.scale_bits;
let capsule = encode_channel(&model, &plan.plaintext)?;
let id = channels.len() as u32;
models.push(model);
channels.push(EntropyChannelDescriptor {
coder: CODER_ORDER0_BYTE_RANS,
coder_version: CODER_VERSION_1,
scale_bits,
lane_count: 1,
model_id: id,
symbol_count: capsule.symbol_count,
decoded_length: capsule.decoded_length,
initial_state: capsule.initial_state,
payload: capsule.payload,
});
channel_id.insert(plan.plaintext.clone(), id);
id
};
let corr = intern(&mut objects, &mut object_id, plan.corrections.clone());
if objects.len() as u64 > u64::from(limits.max_object_count) {
return Ok(None);
}
ops.push(Op::Inline {
bytes: plan.header.to_vec(),
});
ops.push(Op::DeflateReplay {
replay_codec: crate::dra::op::REPLAY_DEFLATE_PREFLATE_0_7_6,
source_kind: DEFLATE_SOURCE_CHANNEL,
source_id: cid,
corrections_object: corr,
declared_output_len: plan.raw_len,
});
ops.push(Op::Inline {
bytes: plan.adler.to_vec(),
});
replayed += 1;
} else {
let start = span.start as usize;
let end = start + span.len as usize;
ops.push(Op::Inline {
bytes: input[start..end].to_vec(),
});
}
if ops.len() as u64 > u64::from(limits.max_graph_ops) {
return Ok(None);
}
}
let format_basis = format!(
"pdf-deflate-replay-rans;streams={};replayed={replayed};channels={};objects={}",
physical.streams.len(),
channels.len(),
objects.len()
);
let descriptor = Descriptor {
universe: UNIVERSE.to_string(),
source_format: SOURCE_FORMAT_PDF,
format_basis,
models,
channels,
objects,
program: Program::new(ops),
source_sha256: sha256(input),
source_len: input.len() as u64,
};
Ok(Some(Candidate {
kind: CandidateKind::PdfDeflateReplayRans,
descriptor,
}))
}
fn intern(objects: &mut Vec<Vec<u8>>, index: &mut HashMap<Vec<u8>, u32>, bytes: Vec<u8>) -> u32 {
if let Some(&id) = index.get(&bytes) {
return id;
}
let id = objects.len() as u32;
index.insert(bytes.clone(), id);
objects.push(bytes);
id
}