Skip to main content

acta/
limits.rs

1//! Bounds applied to sizes a file declares about itself.
2
3/// The default maximum frame header length.
4///
5/// A v0.2 frame header holds a 24- or 64-byte fixed part, 32 bytes per column
6/// descriptor, 48 bytes per stream descriptor, and a statistics area, so this
7/// bound accommodates far more columns than any practical schema.
8const DEFAULT_MAX_FRAME_HEADER_LENGTH: u64 = 64 * 1024 * 1024;
9
10/// The default maximum frame payload length.
11///
12/// The reference block target in specification section 2 is 65,536 rows, so
13/// this bound leaves roughly 64 KiB per row for variable-width values.
14const DEFAULT_MAX_FRAME_PAYLOAD_LENGTH: u64 = 4 * 1024 * 1024 * 1024;
15
16/// The largest number of schema columns accepted by the metadata reader.
17///
18/// Column names and IDs are checked for uniqueness in time linear in the column
19/// count, so this bound exists to cap memory rather than to cap work. It still
20/// admits far more columns than any schema the reference block target suits.
21const DEFAULT_MAX_SCHEMA_COLUMNS: u64 = 65_536;
22
23/// The largest UTF-8 name or encoded type-parameter record accepted before
24/// the reader allocates storage for it.
25const DEFAULT_MAX_SCHEMA_FIELD_LENGTH: u64 = 16 * 1024 * 1024;
26
27/// The largest number of data blocks retained in a reader snapshot.
28const DEFAULT_MAX_BLOCKS: u64 = 10_000_000;
29
30/// The largest logical row count a block decoder will materialize.
31const DEFAULT_MAX_ROWS_PER_BLOCK: u64 = 16 * 1024 * 1024;
32
33/// The largest number of bytes one block decode may materialize in total.
34///
35/// A block decode holds every decoded column at once, and each declaration a
36/// block makes about itself is individually small: a row count, an element
37/// count, a stream length. Decoding multiplies them, so bounding each
38/// declaration on its own still leaves the total unbounded.
39///
40/// A fixed-width value costs its stored bytes and the vector it decodes into,
41/// so roughly twice its logical width for a column with no nulls. This default
42/// therefore admits the reference 65,536-row block of specification section 2
43/// with several hundred columns, and it is the bound to raise for wider or
44/// longer blocks.
45const DEFAULT_MAX_DECODED_BLOCK_BYTES: u64 = 1024 * 1024 * 1024;
46
47/// Bounds checked against declared sizes before any file region is read.
48///
49/// Specification section 2 requires readers to validate length arithmetic for
50/// overflow and recommends configurable resource limits. Each bound is an
51/// inclusive maximum; a larger declared size fails with
52/// [`ErrorKind::ResourceLimit`](crate::ErrorKind::ResourceLimit).
53///
54/// Most bounds describe one structure. Two describe a whole
55/// [`Scan`](crate::Scan) instead, and are spent cumulatively across its blocks:
56/// [`Self::max_rows_per_scan`] and [`Self::max_decoded_scan_bytes`]. Both
57/// default to no limit, so an existing scan cannot acquire a surprising cap,
58/// and both charge only for work the scan really does: a pruned block and an
59/// unprojected column cost nothing. Per-block bounds still apply on top of
60/// them, and [`Reader::read_block`](crate::Reader::read_block) is a single
61/// block decode that never sees a scan's cumulative state.
62///
63/// ```
64/// let limits = acta::Limits::default().with_max_frame_payload_length(1 << 20);
65/// assert_eq!(limits.max_frame_payload_length(), 1 << 20);
66/// ```
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct Limits {
69    max_frame_header_length: u64,
70    max_frame_payload_length: u64,
71    max_schema_columns: u64,
72    max_schema_field_length: u64,
73    max_blocks: u64,
74    max_rows_per_block: u64,
75    max_decoded_block_bytes: u64,
76    max_rows_per_scan: u64,
77    max_decoded_scan_bytes: u64,
78}
79
80impl Limits {
81    /// The largest frame header length this configuration accepts.
82    pub fn max_frame_header_length(&self) -> u64 {
83        self.max_frame_header_length
84    }
85
86    /// The largest frame payload length this configuration accepts.
87    pub fn max_frame_payload_length(&self) -> u64 {
88        self.max_frame_payload_length
89    }
90
91    /// The largest schema column count accepted by metadata parsing.
92    pub fn max_schema_columns(&self) -> u64 {
93        self.max_schema_columns
94    }
95
96    /// The largest column name or encoded type-parameter record accepted by
97    /// metadata parsing.
98    pub fn max_schema_field_length(&self) -> u64 {
99        self.max_schema_field_length
100    }
101
102    /// The largest number of data blocks retained in one reader snapshot.
103    pub fn max_blocks(&self) -> u64 {
104        self.max_blocks
105    }
106
107    /// The largest block row count a logical decoder will materialize.
108    pub fn max_rows_per_block(&self) -> u64 {
109        self.max_rows_per_block
110    }
111
112    /// The largest total number of bytes one block decode may materialize.
113    ///
114    /// The allowance covers every buffer a decode creates, including the
115    /// stored and decompressed stream bytes and the intermediate value
116    /// representations, and it is not refunded when a buffer is released.
117    pub fn max_decoded_block_bytes(&self) -> u64 {
118        self.max_decoded_block_bytes
119    }
120
121    /// The largest number of block rows one scan may decode cumulatively.
122    ///
123    /// A block is charged for the rows it decodes, which is its whole row
124    /// count: a range filter narrows what the scan returns, not what it had to
125    /// materialize to decide. Pruned blocks decode nothing and are not
126    /// charged.
127    pub fn max_rows_per_scan(&self) -> u64 {
128        self.max_rows_per_scan
129    }
130
131    /// The largest number of decoded and intermediate bytes one scan may
132    /// charge cumulatively across all its blocks and selected columns.
133    pub fn max_decoded_scan_bytes(&self) -> u64 {
134        self.max_decoded_scan_bytes
135    }
136
137    /// Return these limits with a different maximum frame header length.
138    pub fn with_max_frame_header_length(mut self, bytes: u64) -> Self {
139        self.max_frame_header_length = bytes;
140        self
141    }
142
143    /// Return these limits with a different maximum frame payload length.
144    pub fn with_max_frame_payload_length(mut self, bytes: u64) -> Self {
145        self.max_frame_payload_length = bytes;
146        self
147    }
148
149    /// Return these limits with a different maximum schema column count.
150    pub fn with_max_schema_columns(mut self, columns: u64) -> Self {
151        self.max_schema_columns = columns;
152        self
153    }
154
155    /// Return these limits with a different maximum schema field length.
156    pub fn with_max_schema_field_length(mut self, bytes: u64) -> Self {
157        self.max_schema_field_length = bytes;
158        self
159    }
160
161    /// Return these limits with a different maximum block count.
162    pub fn with_max_blocks(mut self, blocks: u64) -> Self {
163        self.max_blocks = blocks;
164        self
165    }
166
167    /// Return these limits with a different maximum decoded block row count.
168    pub fn with_max_rows_per_block(mut self, rows: u64) -> Self {
169        self.max_rows_per_block = rows;
170        self
171    }
172
173    /// Return these limits with a different decoded block memory allowance.
174    pub fn with_max_decoded_block_bytes(mut self, bytes: u64) -> Self {
175        self.max_decoded_block_bytes = bytes;
176        self
177    }
178
179    /// Return these limits with a different cumulative scan row allowance.
180    pub fn with_max_rows_per_scan(mut self, rows: u64) -> Self {
181        self.max_rows_per_scan = rows;
182        self
183    }
184
185    /// Return these limits with a different cumulative scan decoded-byte
186    /// allowance.
187    pub fn with_max_decoded_scan_bytes(mut self, bytes: u64) -> Self {
188        self.max_decoded_scan_bytes = bytes;
189        self
190    }
191}
192
193impl Default for Limits {
194    fn default() -> Self {
195        Self {
196            max_frame_header_length: DEFAULT_MAX_FRAME_HEADER_LENGTH,
197            max_frame_payload_length: DEFAULT_MAX_FRAME_PAYLOAD_LENGTH,
198            max_schema_columns: DEFAULT_MAX_SCHEMA_COLUMNS,
199            max_schema_field_length: DEFAULT_MAX_SCHEMA_FIELD_LENGTH,
200            max_blocks: DEFAULT_MAX_BLOCKS,
201            max_rows_per_block: DEFAULT_MAX_ROWS_PER_BLOCK,
202            max_decoded_block_bytes: DEFAULT_MAX_DECODED_BLOCK_BYTES,
203            // A scan is a sequence of independently bounded block decodes.
204            // Keep the cumulative defaults effectively unbounded so existing
205            // readers do not acquire a surprising file-size cap.
206            max_rows_per_scan: u64::MAX,
207            max_decoded_scan_bytes: u64::MAX,
208        }
209    }
210}