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
331
332
333
334
335
336
337
338
//! Taking the whitespace back out of JSON text.
//!
//! What a caller can rely on is documented on [`minify`], which the other entry
//! points here refer back to. Why this shares none of the prettifier's walk,
//! where its one strictness test comes from, and what makes the scan fast is in
//! [docs/design.md](https://github.com/matrix-research-inc/structio/blob/main/docs/design.md#minifying-is-not-the-writer-at-all).
use crate;
use crate;
use crateWriter;
use crate;
use crate;
/// Strip the insignificant whitespace out of a JSON document.
///
/// ```
/// let out = structio::minify("{\n \"a\": [1, 2],\n \"b\": {}\n}").unwrap();
/// assert_eq!(out, r#"{"a":[1,2],"b":{}}"#);
/// ```
///
/// Whitespace inside a string is the document's own and stays:
///
/// ```
/// assert_eq!(structio::minify(r#"[ "a b" ]"#).unwrap(), r#"["a b"]"#);
/// ```
///
/// Laying a document out means knowing its shape, but taking the layout away
/// means knowing only where the strings are: whitespace inside one is the
/// document's, and whitespace outside one is the formatter's. So that is all
/// this looks for. Nothing counts brackets, nothing tracks depth, and no token
/// is read, so a document that is not JSON usually comes back shorter rather
/// than refused. Even a string is measured rather than checked, so a raw
/// control character inside one is copied through like any other byte.
///
/// ```
/// assert_eq!(structio::minify(r#"{"a" : 01, , ,}"#).unwrap(), r#"{"a":01,,,}"#);
/// ```
///
/// A minifier that refused that would be a validator, and
/// [`from_str`](crate::from_str) is already that.
///
/// Nothing, then, is refused for being wrong. Three things are refused for
/// being unanswerable, each as an [`Error`] naming the byte it stopped at. A
/// string that never closes, because there is no telling where it ends. A slash
/// that begins no comment, where [`minify_with`] is reading comments as
/// whitespace, because dropping what follows assumes a comment and keeping it
/// assumes content. And whitespace holding two bare tokens apart, because that
/// whitespace is not the formatter's: removing it would turn `[1 2]` into
/// `[12]`, a different document, and a well-formed one, from input that was
/// neither.
///
/// ```
/// use structio::ErrorCode;
///
/// let e = structio::minify("[1 2]").unwrap_err();
/// assert_eq!(e.code, ErrorCode::UnexpectedCharacter);
/// assert_eq!(e.index, 3);
/// ```
///
/// Every other input either comes out meaning what it meant or comes out as
/// broken as it went in.
///
/// [`prettify_with::<Standard>`](crate::json::prettify_with) minifies too, and
/// agrees with this byte for byte on any document that is actually JSON. It
/// walks the structure to get there, so it costs more and it rejects more.
/// Reach for it when the answer matters as much as the output; reach for this
/// when there is text to shrink.
/// [`minify`] under an explicit [policy](crate::Options).
///
/// There is one minified layout, so the write settings have nothing to say
/// here: [`PRETTY`](Options::PRETTY) and [`INDENT`](Options::INDENT) are not
/// read, and `minify_with::<Pretty>` still minifies. The one setting that
/// applies is [`ALLOW_COMMENTS`](Options::ALLOW_COMMENTS), which decides
/// whether `//` and `/* */` are whitespace. They are dropped like any other
/// whitespace, a comment being no part of what a writer can emit. With the
/// setting on, a slash that begins no comment is refused rather than guessed
/// at; with it off, a slash is an ordinary byte and goes through.
///
/// ```
/// use structio::{AllowComments, json::minify_with};
///
/// let out = minify_with::<AllowComments>("[1, /* two */ 2]").unwrap();
/// assert_eq!(out, "[1,2]");
/// ```
/// [`minify`] into an existing `String`, replacing its contents and keeping its
/// allocation.
///
/// Prefer this in a loop, the way [`write_into`](crate::write_into) is
/// preferred over [`to_string`](crate::to_string).
///
/// On failure `out` holds however much was copied before the error, which is
/// the same bargain [`prettify_into`](crate::prettify_into) makes.
/// [`minify_into`] under an explicit [policy](crate::Options).
/// Copy `data` through, dropping the whitespace between its tokens.
///
/// An error names its own byte, there being no cursor here to ask: the walk is
/// over indices into `data` rather than over a [`Parser`](crate::json::Parser).
/// Index of the first byte at or after `from` that is not simply copied
/// through, or `data.len()` if there is none.
///
/// Three classes stop the copy: whitespace, which is dropped; a quote, which
/// opens a string that has to be measured rather than scanned past; and, under
/// [`Options::ALLOW_COMMENTS`], a slash. Testing "below `0x21`" covers all four
/// whitespace bytes in one operation and sweeps up stray control characters
/// with them, which the caller then passes through unexamined.
/// Largest block [`copy_run`] copies at a time, and so the headroom
/// [`minify_into_with`] leaves on the output buffer.
///
/// The headroom is not what makes the copy safe: `append_fixed` asks for its
/// own room and grows if it has to. It is what keeps that from ever happening,
/// so the one reservation stays the only allocation.
const BLOCK: usize = 64;
/// Copy `data[start..end]` to the output.
///
/// The run goes out as one block of a compile-time-constant size, of which only
/// `end - start` bytes are kept. That lowers to a few wide stores rather than a
/// call to `memcpy` with a length the compiler cannot see, which is worth a
/// great deal here: a minifier copies a document in small pieces, a key or a
/// number or a `true` at a time, and the call would cost more than the bytes.
///
/// Two sizes cover what JSON is made of: sixteen bytes takes a key, a small
/// number or a `true`, and sixty-four takes a double or a string of ordinary
/// length. Anything longer is a string long enough to be worth a real `memcpy`,
/// and so is a run close enough to the end of the input that no block fits
/// behind it. A rung between the two was tried and bought nothing.
/// Copy `N` bytes from `data[start..]` and keep `len` of them, if that is a
/// copy this run wants and the input can supply.
/// Are the `len` bytes at `at` the same as the `len` bytes at `prev`?
///
/// Asked of a run of whitespace against the run before it, which in a laid-out
/// document is the same indentation as often as not. A match means the bytes at
/// `at` are whitespace too, since the run they equal was, so the whole run can
/// be stepped over without looking at it again.
/// Index just past the closing quote of the string whose opening quote is at
/// `open`, or `None` if it never closes.
///
/// The reader's string scan looks for a backslash and a control character
/// alongside the quote, because it has to unescape one and refuse the other.
/// Copying a string through needs neither: an escape is the input's business
/// and goes out as it came in, and the only question is where the string stops.
/// Asking one question instead of three is most of what makes this fast, and
/// strings are most of what a JSON document is.
///
/// A quote closes the string unless an odd number of backslashes escapes it.
/// Counting them backwards is safe because the opening quote is not a
/// backslash, so the walk always stops inside the string.