Skip to main content

caj2pdf_core/
limits.rs

1// SPDX-License-Identifier: MIT
2
3use crate::{Error, Result};
4
5/// Default payload per read or write call: 256 KiB.
6pub const DEFAULT_IO_CHUNK: usize = 256 * 1024;
7/// Hard payload ceiling per read or write call: 1 MiB.
8pub const MAX_IO_CHUNK: usize = 1024 * 1024;
9/// Resource limits applied before allocation and during I/O.
10///
11/// These are the only resource bounds of an operation. Each public entry
12/// point validates them once; format handlers derive every other bound from
13/// these fields or from the format itself.
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct Limits {
16    /// Maximum payload per source/sink call. Must be in `1..=MAX_IO_CHUNK`.
17    pub io_chunk_bytes: usize,
18    /// Maximum selected input bytes: a whole source, PDF range, or sum of fragment spans.
19    pub max_input_bytes: u64,
20    /// Maximum total output bytes for an operation.
21    pub max_output_bytes: u64,
22    /// Maximum single dynamic allocation requested by a format handler. It
23    /// also caps one image payload, one JBIG2 symbol store and one page or
24    /// region bitmap.
25    pub max_allocation_bytes: u64,
26    /// Maximum pages accepted from an input document.
27    pub max_pages: u32,
28    /// Maximum bookmarks accepted from an input document.
29    pub max_bookmarks: u32,
30    /// Maximum pixels of one decoded image, page, region or symbol bitmap.
31    pub max_image_pixels: u64,
32    /// Maximum symbols in one JBIG2 symbol dictionary, including imported
33    /// symbols, and the bound on its height classes and export runs.
34    pub max_symbols: u32,
35}
36
37impl Default for Limits {
38    fn default() -> Self {
39        Self {
40            io_chunk_bytes: DEFAULT_IO_CHUNK,
41            max_input_bytes: 8 * 1024 * 1024 * 1024,
42            max_output_bytes: 16 * 1024 * 1024 * 1024,
43            max_allocation_bytes: 64 * 1024 * 1024,
44            max_pages: 100_000,
45            max_bookmarks: 100_000,
46            max_image_pixels: 12_000_000,
47            max_symbols: 8192,
48        }
49    }
50}
51
52impl Limits {
53    /// Reject invalid configurations before any I/O or allocation occurs.
54    pub fn validate(&self) -> Result<()> {
55        if self.io_chunk_bytes == 0 {
56            return Err(Error::invalid("I/O chunk size must be nonzero"));
57        }
58        if self.io_chunk_bytes > MAX_IO_CHUNK {
59            return Err(Error::limit(
60                "I/O chunk bytes",
61                MAX_IO_CHUNK as u64,
62                self.io_chunk_bytes as u64,
63            ));
64        }
65        self.check_allocation(self.io_chunk_bytes as u64)
66    }
67
68    /// Check the size of a requested single allocation.
69    pub fn check_allocation(&self, bytes: u64) -> Result<()> {
70        if bytes > self.max_allocation_bytes {
71            return Err(Error::limit(
72                "allocation bytes",
73                self.max_allocation_bytes,
74                bytes,
75            ));
76        }
77        Ok(())
78    }
79
80    /// The unlocated error for an allocation of `attempted` bytes that passed
81    /// [`Self::check_allocation`] but was refused by the allocator.
82    pub(crate) fn allocation_refused(&self, resource: &'static str, attempted: u64) -> Error {
83        Error::limit(resource, self.max_allocation_bytes, attempted)
84    }
85
86    /// Check the selected input byte count for one operation.
87    pub fn check_input_size(&self, bytes: u64) -> Result<()> {
88        if bytes > self.max_input_bytes {
89            return Err(Error::limit("input bytes", self.max_input_bytes, bytes));
90        }
91        Ok(())
92    }
93
94    /// Check a page count before indexing or allocating pages.
95    pub fn check_pages(&self, count: u32) -> Result<()> {
96        if count > self.max_pages {
97            return Err(Error::limit(
98                "pages",
99                u64::from(self.max_pages),
100                u64::from(count),
101            ));
102        }
103        Ok(())
104    }
105
106    /// Check the pixel count of one image, page, region or symbol bitmap.
107    pub fn check_image_pixels(&self, pixels: u64) -> Result<()> {
108        if pixels > self.max_image_pixels {
109            return Err(Error::limit("image pixels", self.max_image_pixels, pixels));
110        }
111        Ok(())
112    }
113
114    /// Check the symbol count of one JBIG2 symbol dictionary.
115    pub fn check_symbols(&self, symbols: u64) -> Result<()> {
116        if symbols > u64::from(self.max_symbols) {
117            return Err(Error::limit(
118                "symbols",
119                u64::from(self.max_symbols),
120                symbols,
121            ));
122        }
123        Ok(())
124    }
125
126    /// Check a bookmark count before indexing or allocating bookmarks.
127    pub fn check_bookmarks(&self, count: u32) -> Result<()> {
128        if count > self.max_bookmarks {
129            return Err(Error::limit(
130                "bookmarks",
131                u64::from(self.max_bookmarks),
132                u64::from(count),
133            ));
134        }
135        Ok(())
136    }
137}