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}