mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 22:28:09 +00:00
Merge pull request #5423 from Silvertree2010/fix/5363-eps-import-of-a-large-valid-file
Scale the EPS operation budget with the program's length
This commit is contained in:
@@ -15,8 +15,11 @@ use super::graphics::{GState, Out};
|
||||
use super::lex::{IMMEDIATE, Lexer, number};
|
||||
use super::obj::{Dict, DictRef, Key, Obj, Op, PsError, Res, Shared, ps_err};
|
||||
|
||||
/// Most operations one file may run.
|
||||
/// Most operations one file may run, whatever its length.
|
||||
const MAX_OPS: u64 = 50_000_000;
|
||||
/// More operations a file may run for each byte of its program: a large file that draws a lot
|
||||
/// runs proportionally more, and stays bounded by its length.
|
||||
const OPS_PER_BYTE: u64 = 64;
|
||||
/// Deepest procedure nesting.
|
||||
const MAX_DEPTH: u32 = 400;
|
||||
/// Most operands on the stack.
|
||||
@@ -55,6 +58,8 @@ pub(crate) struct Interp<'a> {
|
||||
resources: DictRef,
|
||||
depth: u32,
|
||||
ops: u64,
|
||||
/// Most operations this program may run: [`MAX_OPS`] and [`OPS_PER_BYTE`] for each byte.
|
||||
max_ops: u64,
|
||||
/// Bytes asked for so far (see [`MAX_MEMORY`]).
|
||||
allocated: usize,
|
||||
seed: u32,
|
||||
@@ -177,6 +182,7 @@ impl<'a> Interp<'a> {
|
||||
resources,
|
||||
depth: 0,
|
||||
ops: 0,
|
||||
max_ops: MAX_OPS.saturating_add((src.len() as u64).saturating_mul(OPS_PER_BYTE)),
|
||||
allocated: 0,
|
||||
seed: 1,
|
||||
saves: vec![],
|
||||
@@ -360,11 +366,11 @@ impl<'a> Interp<'a> {
|
||||
self.spend((n / 256) as u64)
|
||||
}
|
||||
|
||||
/// Count `ops` operations' worth of work against [`MAX_OPS`]: what an operator that costs
|
||||
/// Count `ops` operations' worth of work against the budget (`max_ops`): what an operator that costs
|
||||
/// far more than one operation (laying out type) does in a loop has to end too.
|
||||
pub fn spend(&mut self, ops: u64) -> Res {
|
||||
self.ops = self.ops.saturating_add(ops);
|
||||
if self.ops > MAX_OPS {
|
||||
if self.ops > self.max_ops {
|
||||
return Err(PsError::Limit("the program runs too long"));
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1131,6 +1131,23 @@ fn runs_out_of_budget(body: &str) {
|
||||
assert!(r.warnings.iter().any(|w| w.contains("the program runs too long")), "{body}: {:?}", r.warnings);
|
||||
}
|
||||
|
||||
/// A large file that runs far more operations than a small one may, but only a few hundred for
|
||||
/// each object it draws, reads to its end: the operation budget grows with the program's length.
|
||||
#[test]
|
||||
fn a_large_file_with_many_objects_reads_to_its_end() {
|
||||
let n = 200_000;
|
||||
let mut body = String::from(
|
||||
"/sq { 4 dict begin /y exch def /x exch def 0 1 200 { pop } for \
|
||||
x y moveto x 1 add y lineto x y 1 add lineto closepath fill end } def\n",
|
||||
);
|
||||
for i in 0..n {
|
||||
body.push_str(&format!("{} {} sq\n", i % 100, i / 2000));
|
||||
}
|
||||
let r = read_within(&body, 300);
|
||||
assert!(r.warnings.is_empty(), "{:?}", r.warnings);
|
||||
assert_eq!(objects(&r.document).len(), n);
|
||||
}
|
||||
|
||||
/// A loop of operators whose work grows with the path they make runs out of the operation budget
|
||||
/// (PLRM 3rd ed., chapter 8): `arc` and `arct` with a huge radius, which make thousands of curve
|
||||
/// segments, `strokepath` with a dash far finer than the line, dashes of length zero included, and
|
||||
|
||||
Reference in New Issue
Block a user