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
//! Measurable entry points into work this crate does not otherwise expose.
//!
//! Benches in this workspace drive their crate's public API. Two do not: this
//! module, and `spate-coordination`'s.
//!
//! This crate's public entry point is [`S3Source`](crate::S3Source), which is
//! asynchronous and driven by object-store I/O. Both properties stop an
//! instruction count being deterministic: under a runtime the number becomes
//! a function of how the scheduler interleaved polls. Packing, split identity
//! and record framing are pure, synchronous, on the per-object path and so
//! countable, but reaching them means reaching past the async surface.
//!
//! The rules a future seam has to honor:
//!
//! - It is behind the off-by-default `testing` feature and `#[doc(hidden)]`,
//! so it is not part of this crate's semver surface and no consumer of the
//! `spate` facade can see it.
//! - It exports **functions and the aliases they need, never this crate's own
//! types**. `ObjectFramer`, `ObjectEntry` and the rest stay private and free
//! to change; only the shape of the work is fixed. `MakeFramer` is an alias
//! over `std` and `spate-core` types, which a caller must be able to name to
//! supply a framer at all.
//! - Each function is one whole unit of the work a stage does (a plan, an
//! object run) rather than one internal call. Attribution below that level
//! comes from the callgrind profile the bench already writes.
use crateCompression;
use crateObjectEntry;
use crate;
use crate;
use crateRangeTrim;
use Bytes;
use SplitId;
use RecordFramer;
use black_box;
use io;
use Arc;
/// Builds a fresh record framer, as the source does per object.
pub type MakeFramer = ;
/// Pack a listing into splits, mint each split's id, and encode each
/// descriptor: the whole of what the leader's planner does with a listing,
/// once per plan.
///
/// `objects` is `(key, size_bytes, etag)` **sorted by key**, which is the
/// order `list_all` hands the planner; packing preserves listing order, so
/// feeding it anything else measures a arrangement production never produces.
/// Taken by value so the keys move into the entries rather than being cloned
/// inside the measured region.
///
/// `delimiter` is the framer's resync delimiter, as the source probes it, and
/// packing runs under `Compression::Auto`. With `None` every object is read
/// whole.
///
/// The three stages are one function because the planner does them as one
/// pass per split and a change to any of them moves the others: the id
/// digests the member keys and ETags, and the descriptor serializes the same
/// members immediately afterwards. Splitting them would measure the parts
/// while missing the per-split loop that carries all three, and the
/// serialization is the term most likely to dominate.
///
/// # Panics
///
/// If a packed bin is empty, which `pack` cannot produce, or if a descriptor
/// fails to encode. Neither is reachable from a well-formed listing; both are
/// asserted rather than propagated because a bench has no policy to apply.
/// Frame a run of objects into records through one framer, returning how
/// many records they produced in total.
///
/// Mirrors the lane, which builds a single `ObjectFramer` and cycles
/// `begin_object`/`finish_object` over the members of its split, so a run of
/// several objects here measures the per-object codec resolution and state
/// reset a multi-member split pays, not the cost of constructing a framer
/// per object. One object is a one-element slice.
///
/// Each object is `(key, chunks)`: the key because the codec is resolved from
/// it per object, and the chunks already split the way a fetcher would
/// deliver them, already compressed if `compression` says so, since
/// compressing here would count the compressor rather than the decompressor.
///
/// The framer's record sequence is a pure function of the bytes it is given,
/// so a chunk list starting part-way through a record frames that partial
/// line as a record. [`frame_range`] is the entry a byte-range split takes.
///
/// # Errors
///
/// Whatever the decompressor or the framer reports: a truncated stream, a
/// corrupt frame, or a record over the framer's cap.
/// Read the byte range `[start, end)` of one uncompressed object as a
/// byte-range split does, returning how many records it owns.
///
/// The object is fetched in the windows the fetcher schedules for
/// `range_bytes` and `chunk_bytes`, each copied out of `object` as a GET would
/// deliver it. The owned bytes are trimmed out of each window and framed in
/// `chunk_bytes` pieces through one framer.
///
/// # Errors
///
/// Whatever the framer reports, such as a record over its cap.
///
/// # Panics
///
/// If `start >= end` or `end` is past the object.