Repository navigation
Commit ffdfa3e
authored
Merge commit from fork
Bash keeps a quirk where a brace group followed by a comma set still expands
(`{a},b}`). The parser implements it by rewriting the string and restarting
the scan, absorbing one `}` per pass, so `n` trailing braces cost `n` full
passes:
expand('{a}' + '}'.repeat(128000) + ',z}')
// 27.7 seconds, 2 results
`ms/n^2` is flat and each doubling of `n` costs 4x - quadratic. There is a
second multiplier: each rewrite replaces the group's closing `}` with the
`escClose` sentinel, ~25 characters, so the working string grows by ~25
characters per pass. A 1KB input becomes 26KB, and that ~26x factor multiplies
both the constant and peak memory.
Neither `max` nor `maxLength` could bound it: the cost is in parsing, before
the result set exists, and the payload yields two results at any size.
Bound the number of times the scan may restart (`maxRewrites`, default
`EXPANSION_MAX_REWRITES`, 1000). Past the bound the remaining string is
treated as non-expanding and returned literally, consistent with `max` and
`maxLength`, which truncate rather than throw.
This bounds the number of passes, not the cost of each, so worst-case work is
proportional to `bound x input length` rather than `n x input length`. Spending
the whole budget on a 1MB input measures ~350ms, the same as before the change
for that shape; the quadratic shape drops from 27.7s to ~50ms at n=128,000.
Real `{a},b}` input needs a handful of passes - the existing 30-group
regression test for GHSA-3jxr-9vmj-r5cp is far under the bound.
Verified equivalent to the published release for input below the bound by
differential testing: exhaustive over every string of `{`, `}`, `,` and `a` up
to length 8, plus 300k random inputs with and without `max` / `maxLength` -
387,381 cases, zero mismatches.1 parent c6513ad commit ffdfa3e
2 files changed
Lines changed: 70 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
36 | 46 | | |
37 | 47 | | |
38 | 48 | | |
| |||
114 | 124 | | |
115 | 125 | | |
116 | 126 | | |
| 127 | + | |
117 | 128 | | |
118 | 129 | | |
119 | 130 | | |
| |||
125 | 136 | | |
126 | 137 | | |
127 | 138 | | |
128 | | - | |
| 139 | + | |
129 | 140 | | |
130 | 141 | | |
131 | 142 | | |
| |||
251 | 262 | | |
252 | 263 | | |
253 | 264 | | |
| 265 | + | |
254 | 266 | | |
255 | 267 | | |
256 | 268 | | |
| |||
280 | 292 | | |
281 | 293 | | |
282 | 294 | | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
283 | 298 | | |
284 | 299 | | |
285 | 300 | | |
| |||
311 | 326 | | |
312 | 327 | | |
313 | 328 | | |
314 | | - | |
| 329 | + | |
| 330 | + | |
315 | 331 | | |
316 | 332 | | |
317 | 333 | | |
| |||
350 | 366 | | |
351 | 367 | | |
352 | 368 | | |
353 | | - | |
| 369 | + | |
354 | 370 | | |
355 | 371 | | |
356 | 372 | | |
| |||
389 | 405 | | |
390 | 406 | | |
391 | 407 | | |
392 | | - | |
| 408 | + | |
393 | 409 | | |
394 | 410 | | |
395 | 411 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
0 commit comments