1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-2026 The s3s Authors
//! Boundary delimiter patterns and the data-phase search for them.
use memchr::memmem;
/// Chunks at or below this size keep the old fixed-tail behavior.
const ADAPTIVE_TAIL_THRESHOLD: usize = 2048;
#[derive(Debug, Clone, Copy)]
pub enum DataSearch {
Found { index: usize },
Emit { end: usize },
KeepAll,
}
pub fn search_data(haystack: &[u8], delimiter_finder: &memmem::Finder<'static>) -> DataSearch {
if let Some(index) = delimiter_finder.find(haystack) {
return DataSearch::Found { index };
}
let delimiter = delimiter_finder.needle();
let keep = delimiter.len().saturating_sub(1);
let retain = if haystack.len() > ADAPTIVE_TAIL_THRESHOLD {
// Keep only the longest suffix that could become a delimiter prefix.
// The check is limited to the last delimiter-1 bytes and uses a cheap
// first-byte prefilter before doing any slice comparison.
delimiter_prefix_suffix_len(haystack, delimiter)
} else {
keep
};
if haystack.len() > retain {
DataSearch::Emit {
end: haystack.len() - retain,
}
} else {
DataSearch::KeepAll
}
}
fn delimiter_prefix_suffix_len(haystack: &[u8], delimiter: &[u8]) -> usize {
// Unreachable: `make_delimiter` always produces `\r\n--` plus at least one
// boundary byte, so the delimiter is never empty. The guard keeps an empty
// slice out of the window arithmetic below.
let Some(&first) = delimiter.first() else {
return 0;
};
let max = delimiter.len().saturating_sub(1);
let start = haystack.len().saturating_sub(max);
let window = &haystack[start..];
if memchr::memchr(first, window).is_none() {
return 0;
}
for rel in memchr::memchr_iter(first, window) {
let idx = start.saturating_add(rel);
let len = haystack.len() - idx;
if len <= max && haystack[idx..] == delimiter[..len] {
return len;
}
}
0
}
pub fn make_first_boundary(boundary: &[u8]) -> Box<[u8]> {
let mut pattern = Vec::with_capacity(boundary.len().saturating_add(2));
pattern.extend_from_slice(b"--");
pattern.extend_from_slice(boundary);
pattern.into_boxed_slice()
}
pub fn make_delimiter(boundary: &[u8]) -> Box<[u8]> {
let mut pattern = Vec::with_capacity(boundary.len().saturating_add(4));
pattern.extend_from_slice(b"\r\n--");
pattern.extend_from_slice(boundary);
pattern.into_boxed_slice()
}
pub fn make_delimiter_finder(boundary: &[u8]) -> Box<memmem::Finder<'static>> {
// Build the searcher once so data-path chunks do not reconstruct it on
// every call. Boxing keeps the parser state compact for s3s future sizes.
Box::new(memmem::FinderBuilder::new().build_forward_owned(make_delimiter(boundary)))
}
#[cfg(test)]
#[allow(
clippy::expect_used,
clippy::indexing_slicing,
clippy::panic,
clippy::unreachable,
clippy::unwrap_used
)]
mod tests {
use super::*;
/// `\r\n--boundary` is twelve bytes, so a proper prefix is at most eleven.
const DELIMITER: &[u8] = b"\r\n--boundary";
fn suffix_len(haystack: &[u8]) -> usize {
delimiter_prefix_suffix_len(haystack, DELIMITER)
}
/// The tail analysis must not depend on one delimiter length. For several
/// boundary lengths — the shortest, the longest a boundary may be (70
/// characters), and a few in between — every proper prefix of the delimiter
/// is measured exactly and non-matching tails are not held back, against a
/// brute-force reference.
#[test]
fn delimiter_prefix_suffix_len_handles_every_delimiter_length() {
/// The longest proper prefix of `delimiter` that is a suffix of the
/// haystack, computed by trying every length.
fn oracle(haystack: &[u8], delimiter: &[u8]) -> usize {
let max = delimiter.len().saturating_sub(1).min(haystack.len());
(1..=max)
.rev()
.find(|&len| haystack[haystack.len() - len..] == delimiter[..len])
.unwrap_or(0)
}
for boundary_len in [1usize, 2, 3, 7, 11, 12, 13, 68, 70] {
const ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
let boundary: Vec<u8> = (0..boundary_len).map(|idx| ALPHABET[idx % ALPHABET.len()]).collect();
let delimiter = make_delimiter(&boundary);
let mut cases = vec![Vec::new(), b"data".to_vec(), delimiter.to_vec()];
// Every proper prefix, alone and doubled.
for len in 1..delimiter.len() {
let mut single = b"data".to_vec();
single.extend_from_slice(&delimiter[..len]);
cases.push(single);
let mut doubled = b"data".to_vec();
doubled.extend_from_slice(&delimiter[..len]);
doubled.extend_from_slice(&delimiter[..len]);
cases.push(doubled);
}
// A tail that starts like the delimiter without being a prefix of it.
let mut near_miss = b"data".to_vec();
near_miss.extend_from_slice(&delimiter[..delimiter.len() - 1]);
near_miss.push(b'!');
cases.push(near_miss);
for haystack in cases {
assert_eq!(
delimiter_prefix_suffix_len(&haystack, &delimiter),
oracle(&haystack, &delimiter),
"boundary_len={boundary_len} haystack={haystack:?}"
);
}
}
}
/// F31: every proper prefix of the delimiter must be measured exactly, so a
/// chunk boundary that falls inside the delimiter keeps those bytes back.
#[test]
fn delimiter_prefix_suffix_len_measures_every_proper_prefix() {
for len in 1..DELIMITER.len() {
let mut haystack = b"data".to_vec();
haystack.extend_from_slice(&DELIMITER[..len]);
assert_eq!(suffix_len(&haystack), len, "prefix len={len}");
}
// As a whole suffix the delimiter is longer than `max`, and its first
// byte falls outside the window; `search_data` never asks this anyway
// because a complete delimiter short-circuits to `Found`.
assert_eq!(suffix_len(DELIMITER), 0);
}
/// F31: tails that merely look similar must not be held back, and the
/// first-byte prefilter must not turn a non-match into a match.
#[test]
fn delimiter_prefix_suffix_len_rejects_non_matching_tails() {
assert_eq!(suffix_len(b""), 0);
assert_eq!(suffix_len(b"data"), 0);
// Ends with the delimiter's first byte, but not with a prefix of it.
assert_eq!(suffix_len(b"\r\r\r\r\r"), 1);
// Longer than `max` and only the last byte matches the prefix start.
assert_eq!(suffix_len(b"data\r-\r"), 1);
// A false prefix (`\r\n--C` does not start `\r\n--boundary`).
assert_eq!(suffix_len(b"data\r\n--C"), 0);
// No first byte anywhere in the window: prefilter returns early.
assert_eq!(suffix_len(b"datahello"), 0);
}
/// F31: the adaptive tail only kicks in above the threshold. Exactly
/// `ADAPTIVE_TAIL_THRESHOLD` bytes keep the fixed `delimiter - 1` tail,
/// one byte more switches to the computed suffix.
#[test]
fn search_data_switches_to_the_adaptive_tail_above_the_threshold() {
let finder = make_delimiter_finder(b"boundary");
let keep = DELIMITER.len() - 1;
let at_threshold = vec![b'x'; ADAPTIVE_TAIL_THRESHOLD];
assert!(matches!(
search_data(&at_threshold, &finder),
DataSearch::Emit { end } if end == ADAPTIVE_TAIL_THRESHOLD - keep
));
let above_threshold = vec![b'x'; ADAPTIVE_TAIL_THRESHOLD + 1];
assert!(matches!(
search_data(&above_threshold, &finder),
DataSearch::Emit { end } if end == ADAPTIVE_TAIL_THRESHOLD + 1
));
}
/// F31: a large haystack that ends with a delimiter prefix emits everything
/// before that prefix (the plain fixed tail would hold back more).
#[test]
fn search_data_keeps_only_the_matching_suffix_of_a_large_haystack() {
let finder = make_delimiter_finder(b"boundary");
let mut large = vec![b'x'; ADAPTIVE_TAIL_THRESHOLD * 2];
large.extend_from_slice(&DELIMITER[..5]);
assert!(matches!(
search_data(&large, &finder),
DataSearch::Emit { end } if end == ADAPTIVE_TAIL_THRESHOLD * 2
));
// Below the threshold the fixed tail is kept instead.
let mut small = vec![b'x'; 32];
small.extend_from_slice(&DELIMITER[..5]);
assert!(matches!(
search_data(&small, &finder),
DataSearch::Emit { end } if end == 32 + 5 - (DELIMITER.len() - 1)
));
}
/// F40: `Emit` always hands the caller at least one byte and never more than
/// the haystack — the caller emits `haystack[..end]` and keeps the rest, so
/// an `end` of zero would leave its `while let Some(chunk)` loop spinning
/// without progress. The decision is checked against a reference written
/// straight from the contract (find, else emit all but the longest
/// delimiter-prefix suffix, else keep everything), over haystack lengths
/// around `ADAPTIVE_TAIL_THRESHOLD` and tails that reach the suffix
/// analysis.
#[test]
fn search_data_matches_the_retention_oracle() {
/// Compares results without requiring `PartialEq` on `DataSearch`.
fn key(search: DataSearch) -> (u8, usize) {
match search {
DataSearch::Found { index } => (0, index),
DataSearch::Emit { end } => (1, end),
DataSearch::KeepAll => (2, 0),
}
}
/// Reference: find, else emit all but the longest proper delimiter
/// prefix (or the fixed tail below the threshold), else keep all.
fn oracle(haystack: &[u8], delimiter: &[u8], keep: usize) -> DataSearch {
if let Some(index) = memmem::find(haystack, delimiter) {
return DataSearch::Found { index };
}
let retain = if haystack.len() > ADAPTIVE_TAIL_THRESHOLD {
let max = keep.min(haystack.len());
(1..=max)
.rev()
.find(|&len| haystack[haystack.len() - len..] == delimiter[..len])
.unwrap_or(0)
} else {
keep
};
if haystack.len() > retain {
DataSearch::Emit {
end: haystack.len() - retain,
}
} else {
DataSearch::KeepAll
}
}
let finder = make_delimiter_finder(b"boundary");
let keep = DELIMITER.len() - 1;
let tails: [&[u8]; 7] = [b"", b"x", b"\r", b"\r\n", &DELIMITER[..4], &DELIMITER[..keep], DELIMITER];
let lens = [
0,
1,
keep - 1,
keep,
keep + 1,
64,
ADAPTIVE_TAIL_THRESHOLD - 1,
ADAPTIVE_TAIL_THRESHOLD,
ADAPTIVE_TAIL_THRESHOLD + 1,
ADAPTIVE_TAIL_THRESHOLD * 2,
];
for len in lens {
for tail in tails {
let mut haystack = vec![b'x'; len];
haystack.extend_from_slice(tail);
let got = search_data(&haystack, &finder);
// Invariants first, so a violation names the shape rather than
// only the mismatch.
if let DataSearch::Emit { end } = got {
assert!(end >= 1, "an emit must carry data: len={len} tail={tail:?}");
assert!(end <= haystack.len(), "an emit must stay in range: len={len} tail={tail:?}");
}
if let DataSearch::Found { index } = got {
assert_eq!(
&haystack[index..index + DELIMITER.len()],
DELIMITER,
"a find must be exact: len={len} tail={tail:?}"
);
}
let want = oracle(&haystack, DELIMITER, keep);
assert_eq!(key(got), key(want), "len={len} tail={tail:?} haystack={haystack:?}");
}
}
}
/// F31: a complete delimiter short-circuits to `Found`, and a haystack
/// shorter than the tail is kept whole.
#[test]
fn search_data_reports_found_and_keep_all() {
let finder = make_delimiter_finder(b"boundary");
let mut with_delimiter = vec![b'x'; 40];
with_delimiter.extend_from_slice(DELIMITER);
assert!(matches!(
search_data(&with_delimiter, &finder),
DataSearch::Found { index } if index == 40
));
assert!(matches!(search_data(b"abc", &finder), DataSearch::KeepAll));
assert!(matches!(search_data(b"", &finder), DataSearch::KeepAll));
}
}