Commit 92459cd
webstreams: rewrite ReadableStream, WritableStream, and TransformStream in C++ (zero JS builtins) (oven-sh#33193)
## What
Rewrites the WHATWG Streams implementation (`ReadableStream`,
`WritableStream`, `TransformStream`, all readers/writers/controllers,
`pipeTo`/`pipeThrough`/`tee`, and the async iterator) as pure C++ in
`src/jsc/bindings/webcore/streams/`, and deletes the old implementation
(19 stream JS builtins + 51 old webcore stream C++ files). There are
**zero JS builtins left in the streams path**: no `@`-private JS state,
no builtin closures, no JS-side state machine. All stream state lives as
C++ members on the JS cells themselves (`WriteBarrier` +
`visitChildren`), and all spec "upon fulfillment/rejection" reactions go
through two shared mechanisms (per-global native reaction handlers +
bound functions) instead of per-instance closures.
Bun's extensions are preserved: `type: "direct"` streams, `type:
"bytes"`/BYOB, lazily-materialized native controllers (file/socket/spawn
bodies), `Bun.readableStreamTo*`, `readMany`, and the generated JSSink
classes. String chunks keep working everywhere they used to (verified
old-vs-new on 15 text-chunk scenarios).
## Why
- **Memory** (100k live instances each, RSS/instance, release builds):
| | old | new | |
|---|---|---|---|
| `new ReadableStream({pull(){}})` | 1283 B | **582 B** | −55% |
| RS + `getReader()` | 1709 B | **666 B** | −61% |
| `new WritableStream({write(){}})` | 1718 B | **1036 B** | −40% |
| `new TransformStream()` | 3628 B | **1626 B** | −55% |
The old implementation allocated ~3.1M JS `Function` objects for 400k
streams (the builtins' per-stream algorithm closures); the new one
allocates ~300k — only the user's own callbacks.
- **Throughput** (64 KiB chunks, 32 MiB/pass, best of 5, release vs
release on the same machine;
`bench/snippets/webstreams-throughput.mjs`):
**Stream machinery cost** — the shared-chunk scenarios enqueue the same
64 KiB view every time; default streams pass chunks by reference in
every runtime (nothing is copied), so these rows are reported as
chunks/second (an earlier revision reported them as "MB/s" of nominal
payload — that unit was misleading and is superseded by this table).
Median of 3 isolated processes per cell, measured at the head that
includes the async-iterator/`reader.read()` inlining.
| scenario | node 26 | deno 2.9-canary | bun 1.3.14 | old (canary) | new
| peak RSS node / deno / 1.3.14 / old / new (MB) |
|---|---|---|---|---|---|---|
| `reader.read()` loop | 0.93 M/s | 1.01 M/s | 0.85 M/s | 1.09 M/s |
**1.77 M chunks/s** (565 ns/chunk) | 49 / 59 / 43 / 42 / **38** |
| `for await` | 0.87 M/s | 0.71 M/s | 0.45 M/s | 0.40 M/s | **1.59 M
chunks/s** | 50 / 57 / 47 / 47 / **38** |
| `pipeTo(WritableStream)` | 0.38 M/s | 0.57 M/s | 0.32 M/s | 0.34 M/s |
**0.64 M chunks/s** | 57 / 61 / 49 / 50 / **39** |
| `pipeThrough(TransformStream)` | 0.19 M/s | 0.31 M/s | 0.23 M/s | 0.21
M/s | **0.48 M chunks/s** | 58 / 63 / 56 / 54 / **40** |
| `tee()` + drain both | 0.20 M/s | 0.40 M/s | 0.39 M/s | 0.33 M/s |
**0.75 M chunks/s** | 54 / 60 / 49 / 48 / **38** |
**Real throughput** — the fresh-buffers scenarios allocate and write a
new 64 KiB chunk per enqueue (what socket/file sources produce), so MB/s
is bounded by real memory work and every runtime converges toward
allocator + memcpy bandwidth:
| scenario | node 26 | deno 2.9-canary | bun 1.3.14 | old (canary) | new
|
|---|---|---|---|---|---|
| `reader.read()` loop (fresh buffers) | 6,509 | **6,851 MB/s** | 5,852
| 6,418 | 6,201 |
| `for await` (fresh buffers) | **8,317** | 7,230 | 5,231 | 5,274 |
6,345 |
| `pipeTo` (fresh buffers) | 4,897 | 5,298 | 5,805 | 5,033 | **5,911** |
| `pipeThrough` (fresh buffers) | 4,664 | 5,394 | 5,099 | 4,556 |
**5,400** |
| `tee()` + drain both (fresh buffers) | 4,394 | 5,097 | 5,332 | 4,745 |
**5,982** |
**Consumers and byte sources** (materialize real output; MB/s):
| scenario | node 26 | deno 2.9-canary | bun 1.3.14 | old (canary) | new
| peak RSS 1.3.14 / old / new (MB) |
|---|---|---|---|---|---|---|
| `new Response(stream).arrayBuffer()` | 746 | 1,818 | 8,132 | 7,237 |
**8,127** | 173 / 170 / 199 |
| text chunks → `Response.text()` (Bun extension) | — | — | 8,131 |
8,187 | **8,415** | 204 / 203 / 198 |
| `Bun.readableStreamToBytes(stream)` (Bun extension) | — | — | 8,445 |
6,947 | **8,208** | 173 / 171 / 198 |
| byte source, default reader | 5,578 | 5,935 | 22,997 † | 5,997 |
**7,955** | 43 / 71 / 57 |
| byte source, BYOB reader | 17,835 | 15,912 | 38,373 † | 24,709 † |
15,396 | 45 / 44 / 39 |
| direct stream → `readableStreamToBytes` (Bun extension) | — | — |
2,233 | 3,773 | 3,644 | 218 / 194 / 229 |
**Transform streams** (`bench/snippets/webstreams-transform.mjs`; 16 MiB
per pass in 64 KiB chunks, best of 5, median of 3 isolated processes;
MB/s of decoded/uncompressed payload):
| scenario | node 26 | deno 2.9-canary | bun 1.3.14 | old (canary) | new
|
|---|---|---|---|---|---|
| `TextEncoderStream` | 46 | 389 | 2,429 | 2,442 | **2,717** |
| `TextDecoderStream` | 1,233 | 588 | 1,395 | 1,198 | **1,581** |
| `CompressionStream` (gzip) | 270 | 439 | 447 | 361 | **390** |
| `DecompressionStream` (gzip) | 689 | **1,086** | 926 | 853 | 1,057 |
`TextDecoderStream` is the row the streams machinery dominates; the
compression rows are mostly zlib-bound so the rewrite moves them less
(both are still ahead of the pre-rewrite canary). Note
`CompressionStream` on 1.3.14 (447) vs today's `main` (361) — that ~20%
predates this PR and is unrelated to the streams rewrite.
Methodology: `bench/snippets/webstreams-throughput.mjs` (64 KiB chunks,
32 MiB per pass, best of 5), every scenario in its own process via
`--scenario=`, median of 3 processes per cell on an idle machine; RSS is
the OS-reported peak process RSS (`/usr/bin/time -v`). node v26.3.0,
deno 2.9.1 canary, "old (canary)" = `1.4.0-canary` @ `4f2932980` (main
just before this branch's merge base). † pre-rewrite Bun skipped the
(spec-required) buffer transfers on byte sources — against the runtimes
that also transfer (Node/Deno) the new implementation is faster on the
default-reader row and behind on BYOB (an optimization target; see
follow-ups).
BYOB note: an earlier revision of this table was ~30% behind Node/Deno
on the BYOB row. That gap is closed (parity with Deno, ~97% of Node,
medians of noisy runs) by holding byte-stream buffers as `ArrayBuffer`
impls instead of `JSArrayBuffer` wrapper cells: the spec's two per-chunk
transfers are now contents moves with no new GC cells and no
extra-memory re-reporting; the only cell per chunk is the view handed to
the user. (Old Bun's larger BYOB number is not a valid baseline — it
skipped the spec-required buffer transfers.)
Peak-RSS note (investigated to root cause): three
accumulate-then-assemble consumer rows peak about one payload (~25–35 MB
here) higher than the previous implementation. This is not retention or
a leak: at any instant the new implementation holds exactly one more
*dead* result awaiting collection, because it allocates ~half as many JS
objects per consume (measured: ~3.8k vs ~7.6k for a 512-chunk body), so
JSC's allocation-driven GC runs less often between back-to-back large
consumes. It is bounded at one payload, does not grow with iteration
count, and post-GC RSS is equal or lower than before. Left as-is
deliberately: papering over it with GC hints in the consumer path would
trade real-application throughput for a benchmark's transient peak.
- **Consumers × chunk shape** (8 MiB/pass, MB/s, old → new;
`bench/snippets/webstreams-consumers.mjs`):
| shape | `toText` | `toArrayBuffer` | `toBytes` | `toArray` |
`Response.text` | `Response.arrayBuffer` | `for await` |
|---|---|---|---|---|---|---|---|
| binary 64 KiB ×128 | 2,593 → 3,690 | 6,831 → 7,843 | 6,998 → 8,232 |
63,273 → 81,372 | 2,614 → 3,248 | 7,724 → 7,455 | 32,908 → 115,214 |
| text 64 KiB ×128 | 7,926 → 8,080 | 3,227 → 3,386 | 1,800 → 3,846 |
77,247 → 89,450 | 7,223 → 7,808 | 3,971 → 3,636 | 42,027 → 124,338 |
| mixed 64 KiB ×128 | 2,257 → 3,652 | 4,473 → 4,118 | 4,194 → 4,084 |
84,268 → 109,658 | 2,444 → 3,324 | 4,743 → 4,398 | 49,439 → 107,137 |
| binary 1 KiB ×8192 | 1,041 → 954 | 1,023 → 1,523 | 1,115 → 1,712 |
1,455 → 2,483 | 1,117 → 1,130 | 1,147 → 1,485 | 867 → 2,057 |
| text 1 KiB ×8192 | 1,414 → 1,588 | 970 → 1,375 | 952 → 1,206 | 1,319 →
2,121 | 1,449 → 1,905 | 721 → 1,348 | 889 → 2,524 |
| one 8 MiB chunk | 1,959 → 5,084 | ≈2 TB/s (identity) | ≈2 TB/s | ≈2
TB/s | 1,955 → 5,213 | ≈2 TB/s | ≈2 TB/s |
The buffered consumers are driven by a persistent pump operation (one
reaction registration per pending read; bulk queue drain per hop), which
is what recovered the many-small-chunk rows. Remaining cells within
run-to-run noise of the previous implementation: binary-1 KiB `toText`
and a few 64 KiB `arrayBuffer` cells (±10%).
- **Memory** (`bench/snippets/webstreams-memory.mjs`, release builds):
| retained RSS per live instance (100k) | old | new |
|---|---|---|
| `new ReadableStream({pull(){}})` | 1,307 B | **566 B** |
| RS + `getReader()` | 1,680 B | **676 B** |
| `new WritableStream({write(){}})` | 1,722 B | **1,034 B** |
| `new TransformStream()` | 3,635 B | **1,596 B** |
| workload RSS | old peak | new peak |
|---|---|---|
| `pipeTo` 512 MiB (64 KiB chunks) | 48.8 MB | **17.0 MB** |
| `for await` 512 MiB | 2.5 MB | **0.5 MB** |
| `Response(stream 256 MiB).arrayBuffer()` | 254.5 MB | 253.1 MB |
| `Response(stream 256 MiB of text).text()` | 256.5 MB | 249.4 MB |
The implementation allocates no closures per stream (~3.1M `Function`
objects → ~300k for 400k streams: only the user's own callbacks remain).
- **`tee()` / `clone()`** (`bench/snippets/webstreams-tee.mjs`; 128 MiB
tee payload, 64 MiB fetched body, 32 MiB uploaded body):
| scenario | old | new |
|---|---|---|
| `tee()`: both branches drained concurrently | 57,731 MB/s | **206,635
MB/s** |
| `tee()`: branch B read only after A finishes | 84,543 MB/s | **151,754
MB/s** |
| `tee()`: read A, cancel B | 48,141 MB/s | **90,148 MB/s** |
| `fetch().clone()`: read both bodies | 747 MB/s / 210 MB peak | **1,237
MB/s / 49 MB peak** |
| `fetch().clone()`: read one, cancel clone | 643 MB/s | 710 MB/s |
| `Bun.serve`: `req.clone()`, read both bodies | 1,423 MB/s / 32 MB peak
| **1,466 MB/s / 1.5 MB peak** |
The one remaining row below the previous implementation anywhere in
these tables is direct streams → `readableStreamToBytes` (−20%), tracked
in the checklist.
- **Spec conformance**, measured with the WPT streams suite vendored in
this PR (69 `.any.js` files including `idlharness.any.js`, 1402
subtests, run in CI):
- old implementation (behavior files only): **971/1174 passing
(82.7%)**, 2 process-aborting crashes, 10 timeouts
- this PR: **1402/1402 passing (100%)**, 0 crashes, 0 timeouts, and
`expectations.json` is empty — no expected-failure list. This includes
WPT's `idlharness.any.js` (228 WebIDL surface subtests: interface-object
descriptors, prototype layout, method `length`/`name`, brand checks),
the same file Node runs, vendored with `idlharness.js` + the webidl2
parser + the `.idl` definitions from the same WPT commit. idlharness
found one real bug, fixed here: the streams interface objects were
installed as *enumerable* globals (Web IDL: `{writable: true,
enumerable: false, configurable: true}`; Node and the browsers comply) —
they are now `DontEnum` like `URL` already was. The other non-streams
constructors (`Response`, `Blob`, …) have the same pre-existing issue
and are a separate follow-up.
- The last formerly-pinned behavior subtest turned out to be a
harness-shim divergence, not a stream bug: upstream `testharness.js`'s
`assert_object_equals` recurses into any non-null object on the *actual*
side, so its `{value: <empty Uint8Array>}` vs `{value: undefined}`
comparison is vacuous — the semantics every browser and Node run under.
Our shim was stricter; it now ports upstream byte-for-byte. Bun's
behavior for that case (cancel then `read(view)` resolving with a
zero-length view) matches the WHATWG algorithm text, the reference
implementation, Node, and Deno.
- Scope vs Node/Deno: the same upstream `streams/` test files both run
in their WPT CI, minus the `.tentative` `owning-type` proposal (Node
expected-fails it) and `transferable/**` (postMessage stream transfer, a
feature Bun does not implement). Node and Deno each still carry
expected-failure lists on this suite; this PR's list is empty.
- **Maintainability**: the implementation is a file-per-class
transcription of the spec, so every function maps to a named spec
operation.
## Issues this closes
Each one verified by running the issue's own reproduction on the
pre-rewrite build (bug reproduces) and on this branch (fixed):
- oven-sh#3700 / oven-sh#32529 — `ReadableStream.from()` (global and
`node:stream/web`)
- oven-sh#6860 — reusing a consumed `ReadableStream` now always rejects
(`Bun.readableStreamTo*` on a disturbed, unlocked stream rejects with
"ReadableStream has already been used"; the spec'd `new
Response(stream)` path already threw)
- oven-sh#7091 — `ReadableStreamBYOBReader.read(view, { min })`
- oven-sh#10431 — `values({ preventCancel: true })`
- oven-sh#17081 — `ReadableStreamBYOBReader.releaseLock()` with pending reads
(rejects them per spec instead of throwing)
- oven-sh#26392 — `pipeTo` honors `AbortSignal` (sink `abort()` and source
`cancel()` both run)
- oven-sh#31156 — `WritableStreamDefaultController.signal`
- oven-sh#32402 — BYOB `read(view)` detaches the supplied buffer
- oven-sh#17837 — `ReadableStreamDefaultController` methods failing brand
checks ("`this` is not a ReadableStreamDefaultController"): no
deterministic repro exists in the issue, but the mechanism that produced
it (the JS-builtin controllers) no longer exists; every controller is a
real C++ class now.
(oven-sh#19006 from the same triage list is not claimed: its repro already
passes on current `main`.)
Fixes oven-sh#3700
Fixes oven-sh#6860
Fixes oven-sh#7091
Fixes oven-sh#10431
Fixes oven-sh#17081
Fixes oven-sh#17837
Fixes oven-sh#26392
Fixes oven-sh#31156
Fixes oven-sh#32402
Fixes oven-sh#32529
## Review feedback round (2026-07-03)
All applied on top of the rewrite, each with tests where behavior
changed:
1. **Identifier caching**: every `Identifier::fromString` in the streams
sources (71 sites, several per-chunk) now uses `vm.propertyNames` /
`BunBuiltinNames`; the three helpers that took a method-name literal
take `const Identifier&`.
2. **Replacement-character audit**: no defects — WTF's default lenient
UTF-8 conversion is the FFFD-replacing conversion (`StringImpl.cpp`'s
conversion modes share the same case), and the mixed text path
byte-concatenates before decoding once, so multi-byte sequences split
across chunks decode correctly. The two equivalent encoders were unified
on the simdutf sizer/writer pair.
3. **Accumulator retention**: the text accumulator (including the one on
the long-lived direct-stream controller), the array sink's result array,
and the internal chunk arrays held by conversion reactions are all
released the moment the result is materialized.
4. **String limits**: text assembly past the string limit used to abort
the process (StringBuilder overflow /
`fromUTF8ReplacingInvalidSequences`'s RELEASE_ASSERT); every
materialization site now throws a catchable out-of-memory error using
the same predicate Bun's string constructors use, with subprocess
regression tests via the `bun:internal-for-testing` synthetic allocation
limit.
5. **InternalFieldTuples**: the one nested (3-field) case is a dedicated
`JSReadableStreamIntoArrayOperation` cell; the one-shot sink's
close-function tuple and pipeTo's wait-for-all latch tuple are fields on
their existing cells. The remaining tuples are genuine 2-field pairs.
6. **Single-pass iteration**: the chunk-array converters read the array
exactly once (elements held in a `MarkedArgumentBuffer`, strings
materialized and sized once); no user-provided iterable was ever
iterated twice (`ReadableStream.from` builds one iterator record).
7. **`VM&` threading**: internal helpers across the streams sources take
`JSC::VM&` from their callers instead of re-deriving it from the global
object; entry points derive it once.
## Behavior changes (intentional)
- Reusing an already-consumed `ReadableStream` with
`Bun.readableStreamTo*` now rejects (`ERR_INVALID_STATE`,
"ReadableStream has already been used") instead of resolving with an
empty result (oven-sh#6860). The previous implementation only errored while the
stream was still locked.
- `new TextDecoderStream(label, undefined | null)` treats the options as
an empty dictionary per Web IDL (previously threw).
- The streams interface objects on `globalThis` are non-enumerable, per
Web IDL (matches Node and the browsers; found by WPT's `idlharness`).
- Releasing a reader/writer lock now follows the current spec **and
Node.js**: pending reads, `reader.closed`, `writer.closed`/`ready`, and
post-release method calls reject with the same `TypeError` (+ `code:
"ERR_INVALID_STATE"`, same messages) that Node 26 produces — verified
case-by-case against Node. The old Bun-specific `AbortError` with `code:
"ERR_STREAM_RELEASE_LOCK"` is no longer produced (nothing in the tree
produced or consumed it after the old builtins were removed;
`process.stdin`'s internals were updated accordingly).
- `Bun.readableStreamTo*` are now regular native functions on `Bun`
(previously configurable JS builtins).
## Tests
- `test/js/third_party/wpt-streams/`: the vendored WPT streams suite +
expectations (re-recorded against this implementation; expected-failure
bodies still execute, so both regressions and silent graduations turn
the suite red).
- New regression tests for byte-source `pipeTo`/`pipeThrough` (not
covered by any WPT subtest) in `test/js/web/streams/streams.test.js`;
the `releaseLock` test there was updated from the old
implementation-specific error shape to the Node/spec shape (see behavior
changes).
- `bench/snippets/webstreams.mjs`, `webstreams-throughput.mjs`,
`webstreams-memory.mjs`: the benchmarks behind the numbers above.
- The wider Bun test-suite sweep is in progress on this branch.
## Status
- [x] implementation + integration, old implementation deleted
- [x] WPT streams suite vendored (including `idlharness.any.js`):
1402/1402 subtests passing, `expectations.json` empty
- [x] node-compat pass: reader/writer release, locked-acquisition,
brand-check, byte- and default-controller and BYOB-request error shapes
match Node 26 (codes + messages); promise semantics match Node
observably
- [x] Bun extension semantics preserved and tested:
async-iterable/async-generator bodies (direct controller via `yield`,
sink backpressure, iterator `throw`/`return`), `reader.readMany()`,
direct streams, `AsyncLocalStorage` propagation into source callbacks
- [x] buffered consumers ≥ the previous implementation on every chunk
shape (persistent pump op)
- [x] exception-check discipline: suites run clean under
`BUN_JSC_validateExceptionChecks=1`
- [x] `tee()` / `Response.clone()` / `Request.clone()` faster with lower
peak RSS
- [x] release-build crash + hang fixed for a direct stream whose reader
is released mid-pull
- [ ] CI: full matrix on the latest push
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>1 parent 5187e27 commit 92459cd
276 files changed
Lines changed: 54660 additions & 14571 deletions
File tree
- bench/snippets
- scripts
- build
- src
- codegen
- jsc
- bindings
- webcore
- streams
- js
- builtins
- internal
- sql
- streams
- runtime
- server
- webcore
- test/js
- bun
- http
- util
- node/process
- third_party
- wpt-h2
- wpt-streams
- common
- interfaces
- resources
- webidl2/lib
- streams
- piping
- readable-byte-streams
- crashtests
- readable-streams
- crashtests
- resources
- transform-streams
- writable-streams
- crashtests
- web
- encoding
- fetch
- streams
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
198 | 198 | | |
199 | 199 | | |
200 | 200 | | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 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 | + | |
| 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 | + | |
| 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 | + | |
| 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 | + | |
| 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 | + | |
| 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 | + | |
0 commit comments