@amritk/nish-x86_64-linux 0.18.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/INSTALL.md +58 -28
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +3 -2
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +99 -5
  22. package/runtime/nish.h +289 -19
  23. package/runtime/nish.mjs +39 -3
  24. package/runtime/node.mjs +383 -0
  25. package/runtime/runtime-aes.c +490 -0
  26. package/runtime/runtime-bytes.c +401 -0
  27. package/runtime/runtime-cross.c +366 -0
  28. package/runtime/runtime-host.c +8 -7
  29. package/runtime/runtime-net.c +93 -34
  30. package/runtime/runtime-os.c +214 -16
  31. package/runtime/runtime-parallel.c +590 -107
  32. package/runtime/runtime-simd.c +235 -11
  33. package/runtime/runtime-term.c +504 -0
  34. package/runtime/runtime-wasm.c +5 -14
  35. package/runtime/runtime.c +257 -17
  36. package/runtime/shim.mjs +6 -915
  37. package/runtime/sqlite/LICENSE.md +85 -0
  38. package/runtime/sqlite/README.md +45 -0
  39. package/runtime/sqlite/nish-sqlite.c +359 -0
  40. package/runtime/sqlite/sqlite3.c +271131 -0
  41. package/runtime/sqlite/sqlite3.h +14447 -0
  42. package/scripts/build.sh +437 -28
  43. package/std/LICENSE-fast-float +23 -0
  44. package/std/LICENSE-fdlibm +8 -0
  45. package/std/README.md +108 -58
  46. package/std/base64.ts +116 -0
  47. package/std/bytes.ts +419 -0
  48. package/std/cli.ts +328 -0
  49. package/std/collections.ts +6 -5
  50. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  51. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  52. package/std/crypto/aes.ts +238 -76
  53. package/std/crypto/base64url.ts +125 -0
  54. package/std/crypto/bcrypt.ts +760 -0
  55. package/std/crypto/chacha20poly1305.ts +43 -9
  56. package/std/crypto/ed25519.ts +840 -0
  57. package/std/crypto/hkdf.ts +304 -12
  58. package/std/crypto/hmac.ts +382 -54
  59. package/std/crypto/p256.ts +932 -61
  60. package/std/crypto/x25519.ts +55 -37
  61. package/std/crypto/x509-random.ts +71 -0
  62. package/std/crypto/x509.ts +20 -131
  63. package/std/json-tokenizer.ts +4237 -0
  64. package/std/json-typed.ts +1014 -0
  65. package/std/json-value.ts +1182 -0
  66. package/std/json.ts +1202 -34
  67. package/std/math.ts +608 -0
  68. package/std/net/buffer-pool.ts +151 -0
  69. package/std/net/hpack.ts +416 -112
  70. package/std/net/http-fields.ts +93 -0
  71. package/std/net/http1-server.ts +501 -62
  72. package/std/net/http1.ts +31 -6
  73. package/std/net/http2-tls.ts +112 -24
  74. package/std/net/http2.ts +333 -75
  75. package/std/net/http3-server.ts +261 -43
  76. package/std/net/http3.ts +187 -28
  77. package/std/net/postgres.ts +1875 -0
  78. package/std/net/prefork.ts +412 -0
  79. package/std/net/quic-listener.ts +108 -11
  80. package/std/net/quic-packet.ts +243 -82
  81. package/std/net/quic-recovery.ts +361 -30
  82. package/std/net/quic-stream.ts +424 -24
  83. package/std/net/quic.ts +519 -167
  84. package/std/net/router.ts +676 -0
  85. package/std/net/tls/codec.ts +426 -27
  86. package/std/net/tls/record-server.ts +478 -132
  87. package/std/net/tls/record.ts +133 -42
  88. package/std/net/tls/schedule.ts +464 -55
  89. package/std/net/tls/ticket.ts +610 -0
  90. package/std/net/tls-tcp.ts +196 -53
  91. package/std/net/tls.ts +1018 -260
  92. package/std/net/url.ts +571 -0
  93. package/std/random.ts +94 -0
  94. package/std/regex.ts +4274 -0
  95. package/std/sqlite.ts +391 -0
  96. package/std/term-io.ts +978 -0
  97. package/std/term-pixels.ts +874 -0
  98. package/std/term-probe.ts +459 -0
  99. package/std/term-rate.ts +993 -0
  100. package/std/term.ts +2453 -0
  101. package/std/text.ts +90 -7
  102. package/std/threads.ts +438 -14
  103. package/std/yaml-lines.ts +141 -0
  104. package/std/yaml.ts +4675 -0
@@ -23,12 +23,25 @@
23
23
  * **Fixed slots.** The table is sized once, from the configuration: one slot
24
24
  * per stream that may be open at once — as many client-opened bidirectional
25
25
  * and unidirectional streams as this side lets the client have, and
26
- * `localStreams` of its own — each with a receive buffer and a send buffer
27
- * of `bufferSize` bytes and a bit per byte beside each. A stream lives in
28
- * its slot from the frame (or `open`) that starts it until both of its
29
- * sides are done, and then the slot is free for the next. A stream is found
30
- * by its ID through a hash index, never by a scan. Nothing a peer sends, and
31
- * nothing the application writes, allocates.
26
+ * `localStreams` of its own. A stream lives in its slot from the frame (or
27
+ * `open`) that starts it until both of its sides are done, and then the slot
28
+ * is free for the next. A stream is found by its ID through a hash index,
29
+ * never by a scan.
30
+ *
31
+ * **Lent buffers.** A slot holds no buffers of its own. A side borrows a
32
+ * `bufferSize`-byte buffer, and a bit per byte beside it, from the table's
33
+ * `QuicStreamPools` when it first needs one: the receive side on the first
34
+ * byte that lands, the send side on the first byte written. Each goes back,
35
+ * its used extent zeroed, once it is done with: the receive side once the
36
+ * application has read to the final size or the stream is reset, the send
37
+ * side once every byte up to the final size is acknowledged or the stream
38
+ * is reset, never before, since a lost byte is resent from the send buffer.
39
+ * The pools hold two buffers per slot at most, the reserve's included, so a
40
+ * borrow never fails, and
41
+ * a server shares one pool across all its connections (`share`), so what it
42
+ * holds follows the streams moving bytes, not the slots it was sized for.
43
+ * Once the pools are warm, nothing a peer sends and nothing the application
44
+ * writes allocates.
32
45
  *
33
46
  * **Receiving** (§3.2, §2.2). Data lands in the receive buffer at its offset
34
47
  * modulo the buffer's size, in any order and overlapping, with a bit set per
@@ -84,6 +97,7 @@ import {
84
97
  quicPutValue,
85
98
  quicStreamOverhead,
86
99
  } from "nish/net/quic-frame"
100
+ import { BufferPool } from "nish/net/buffer-pool"
87
101
 
88
102
  // ---- What the stream calls answer -----------------------------------------------
89
103
 
@@ -197,6 +211,186 @@ export const quicStreamSetBit = (bits: u8[], k: i32, on: boolean): void => {
197
211
  /** The smaller of two `i64`s. */
198
212
  const quicStreamMin = (a: i64, b: i64): i64 => (a < b ? a : b)
199
213
 
214
+ /**
215
+ * The buffers a connection's streams borrow: `data` lends the receive and
216
+ * send buffers of `bufferSize` bytes, `bits` the bitmaps beside them, a bit
217
+ * per byte. One table makes a private set for itself; a server makes one set
218
+ * for every slot and hands it to each connection's table with
219
+ * `QuicStreams.share`, so the memory follows the streams moving bytes rather
220
+ * than the slots the server was sized for. Each pool's ceiling is two
221
+ * buffers for every stream it serves, one a side, which is what holding
222
+ * every buffer at once costs. The reserve below counts against it too, so a
223
+ * send side that finds the pools out takes the reserve's last buffer
224
+ * (`lendSend`): with that side holding none, not every buffer is lent, and
225
+ * the one left over is in the reserve.
226
+ *
227
+ * Frames are handled inside the arena scope a packet is opened in, where
228
+ * nothing may allocate, grow a list or push onto one. So the frames lend and
229
+ * take back receive buffers through a reserve the pools keep beside their
230
+ * free lists, by index alone: `lend` hands one out, `keep` takes one back
231
+ * zeroed. Before each scope `prime` trims the reserve to `hold` buffers,
232
+ * giving the rest to the pools, and tops it up to as many as the packet
233
+ * could start receive sides, taking them. `hold` is one connection's stream
234
+ * count, and the reserve three times that long, so a packet that lends
235
+ * nothing and takes back every buffer its connection held still fits. Once
236
+ * warm, the reserve mostly circulates on its own and `prime` costs a handful
237
+ * of comparisons, with no take or give. The pools count the reserve's
238
+ * buffers as lent. `prime` and the `drain` before each give-back outside a
239
+ * packet trim it to `hold` for the whole server, not each connection, but
240
+ * `keep` inside a packet can raise it to three times `hold`, and it stays
241
+ * there until the next `prime` or `drain`.
242
+ */
243
+ export class QuicStreamPools {
244
+ data: BufferPool
245
+ bits: BufferPool
246
+ /** The reserve: `ready` buffers and their bitmaps, taken from the pools and not lent to a stream; the rest of each list is `none`. */
247
+ readyData: u8[][]
248
+ readyBits: u8[][]
249
+ /** The empty array a free reserve entry holds. */
250
+ none: u8[]
251
+ /** How many buffers the reserve holds. */
252
+ ready: i32 = 0
253
+ /** The most the reserve keeps from one packet to the next: one connection's stream count. */
254
+ hold: i32 = 0
255
+ /** How many buffers each pool's free list has been grown to hold, by `fit`. */
256
+ room: i32 = 0
257
+
258
+ /** Pools for `connections` connections of `streams` stream slots each, `bufferSize` bytes each way. */
259
+ constructor(bufferSize: i32, streams: i32, connections: i32) {
260
+ this.data = new BufferPool("QuicStreams.data", bufferSize, streams * 2 * connections)
261
+ this.bits = new BufferPool("QuicStreams.bits", (bufferSize + 7) >> 3, streams * 2 * connections)
262
+ this.none = []
263
+ this.hold = streams
264
+ this.readyData = []
265
+ this.readyBits = []
266
+ for (let k: i32 = 0; k < streams * 3; k += 1) {
267
+ this.readyData.push(this.none)
268
+ this.readyBits.push(this.none)
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Readies the reserve for a packet that could start `need` receive sides:
274
+ * trims it to `hold` buffers, then tops it up to `need`. Takes and gives
275
+ * only when it is outside that window. Never inside an arena scope.
276
+ */
277
+ prime(need: i32): void {
278
+ this.drain(this.hold)
279
+ while (this.ready < need && this.ready < toI32(this.readyData.length)) {
280
+ this.readyData[this.ready] = this.data.take()
281
+ this.readyBits[this.ready] = this.bits.take()
282
+ this.ready = this.ready + 1
283
+ }
284
+ this.fit()
285
+ }
286
+
287
+ /** Gives the reserve's buffers past the first `most` back to the pools, each already zeroed by `keep`. Never inside an arena scope. */
288
+ drain(most: i32): void {
289
+ while (this.ready > most) {
290
+ this.ready = this.ready - 1
291
+ this.data.give(this.readyData[this.ready], QUIC_STREAM_NO_BYTES)
292
+ this.bits.give(this.readyBits[this.ready], QUIC_STREAM_NO_BYTES)
293
+ this.readyData[this.ready] = this.none
294
+ this.readyBits[this.ready] = this.none
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Grows each pool's free list until it can hold every buffer the pool has
300
+ * made, by pushing `none` and popping it straight off again, so that a
301
+ * give-back never grows it. A slot's `reset` gives back every buffer its
302
+ * connection held at once, more than the free list ever held before, and
303
+ * the arena must not move by it (QUIC-3). The room doubles, so the pushes
304
+ * cost O(1) for each buffer made.
305
+ */
306
+ fit(): void {
307
+ const made: i32 = this.data.made
308
+ if (made <= this.room) {
309
+ return
310
+ }
311
+ let target: i32 = this.room + this.room
312
+ if (target < made) {
313
+ target = made
314
+ }
315
+ if (target > this.data.ceiling) {
316
+ target = this.data.ceiling
317
+ }
318
+ quicStreamGrowFree(this.data.free, target, this.none)
319
+ quicStreamGrowFree(this.bits.free, target, this.none)
320
+ this.room = target
321
+ }
322
+
323
+ /** Lends the reserve's last buffer, into `stream`'s receive side. Panics when the reserve is empty: a packet handled without `prime`. */
324
+ lend(stream: QuicStream): void {
325
+ if (this.ready <= 0) {
326
+ panic("QuicStreams: a receive buffer was wanted with none primed")
327
+ }
328
+ this.ready = this.ready - 1
329
+ stream.recvBuf = this.readyData[this.ready]
330
+ stream.recvHave = this.readyBits[this.ready]
331
+ this.readyData[this.ready] = this.none
332
+ this.readyBits[this.ready] = this.none
333
+ }
334
+
335
+ /**
336
+ * Lends `stream` a send buffer and bitmap: taken from the pools while they
337
+ * have one to give, else the reserve's last. The reserve counts against
338
+ * the same ceiling, so once the pools have made every buffer and lent them
339
+ * all, a send side wanting one means the reserve holds it. Never inside an
340
+ * arena scope, where a take would allocate.
341
+ */
342
+ lendSend(stream: QuicStream): void {
343
+ if (this.data.free.length === 0 && this.data.made >= this.data.ceiling && this.ready > 0) {
344
+ this.ready = this.ready - 1
345
+ stream.sendBuf = this.readyData[this.ready]
346
+ stream.sendAcked = this.readyBits[this.ready]
347
+ this.readyData[this.ready] = this.none
348
+ this.readyBits[this.ready] = this.none
349
+ return
350
+ }
351
+ stream.sendBuf = this.data.take()
352
+ stream.sendAcked = this.bits.take()
353
+ this.fit()
354
+ }
355
+
356
+ /**
357
+ * Takes `buf` and its bitmap `have` back into the reserve, zeroing the
358
+ * first `used` bytes of the buffer and the bits of them. Safe inside an
359
+ * arena scope. Panics when the reserve is full: more given back between
360
+ * two `drain`s than one connection holds, a table's bug.
361
+ */
362
+ keep(buf: u8[], have: u8[], used: i32): void {
363
+ if (this.ready >= toI32(this.readyData.length)) {
364
+ panic(`QuicStreamPools: the reserve of ${this.readyData.length} buffers is full`)
365
+ }
366
+ buf.fill(0, 0, used)
367
+ have.fill(0, 0, (used + 7) >> 3)
368
+ this.readyData[this.ready] = buf
369
+ this.readyBits[this.ready] = have
370
+ this.ready = this.ready + 1
371
+ }
372
+
373
+ /** How many buffers and bitmaps are lent to streams now, the reserve's left out: 0 once every stream that borrowed has given back. */
374
+ lent(): i32 {
375
+ return this.data.lent() + this.bits.lent() - this.ready - this.ready
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Pushes `none` onto `free` until it is `target` long, then pops each one off
381
+ * again: the capacity stays, so giving back that many buffers to the pool the
382
+ * list belongs to never grows the arena.
383
+ */
384
+ export const quicStreamGrowFree = (free: u8[][], target: i32, none: u8[]): void => {
385
+ const length: i32 = toI32(free.length)
386
+ while (toI32(free.length) < target) {
387
+ free.push(none)
388
+ }
389
+ while (toI32(free.length) > length) {
390
+ free.pop()
391
+ }
392
+ }
393
+
200
394
  /**
201
395
  * One stream slot. `id` is -1 while the slot is free. The fields are
202
396
  * readable — the states, `resetCode` once the peer reset it, `stopCode` once
@@ -204,7 +398,11 @@ const quicStreamMin = (a: i64, b: i64): i64 => (a < b ? a : b)
204
398
  */
205
399
  export class QuicStream {
206
400
  id: i64 = -1
207
- /** The receive buffer: byte `o` of the stream at `o % size`, its bit in `recvHave` set while unread. */
401
+ /**
402
+ * The receive buffer: byte `o` of the stream at `o % size`, its bit in
403
+ * `recvHave` set while unread. Both are the table's empty array until the
404
+ * first byte arrives and after the side is done, and borrowed in between.
405
+ */
208
406
  recvBuf: u8[]
209
407
  recvHave: u8[]
210
408
  /** How far the application has read. */
@@ -221,7 +419,12 @@ export class QuicStream {
221
419
  resetCode: i64 = -1
222
420
  /** The STOP_SENDING error code this side asked with, or -1. */
223
421
  stopSendingCode: i64 = -1
224
- /** The send buffer: byte `o` at `o % size`, its bit in `sendAcked` set once acknowledged and not yet released. */
422
+ /**
423
+ * The send buffer: byte `o` at `o % size`, its bit in `sendAcked` set once
424
+ * acknowledged and not yet released. Borrowed by the first write, and kept
425
+ * until every byte is acknowledged or the side is reset, since a lost
426
+ * packet is sent again from it.
427
+ */
225
428
  sendBuf: u8[]
226
429
  sendAcked: u8[]
227
430
  /** Every byte below this was acknowledged, and its room released. */
@@ -242,6 +445,8 @@ export class QuicStream {
242
445
  stopCode: i64 = -1
243
446
  /** The limit the last STREAM_DATA_BLOCKED named, or -1. */
244
447
  blockedAt: i64 = -1
448
+ /** The bytes of each buffer once borrowed, which the offsets wrap at whether or not one is. */
449
+ size: i32 = 0
245
450
  sendState: i32 = 0
246
451
  recvState: i32 = 0
247
452
  /** Whether the application wrote the FIN, a frame carried it, and the peer acknowledged it. */
@@ -259,17 +464,18 @@ export class QuicStream {
259
464
  /** Whether the slot is in the event ring. */
260
465
  queued: boolean = false
261
466
 
262
- constructor(bufferSize: i32) {
263
- this.recvBuf = new Array<u8>(bufferSize)
264
- this.recvHave = new Array<u8>((bufferSize + 7) >> 3)
265
- this.sendBuf = new Array<u8>(bufferSize)
266
- this.sendAcked = new Array<u8>((bufferSize + 7) >> 3)
467
+ /** A slot for buffers of `bufferSize` bytes, holding none: each side starts as `none`, the table's empty array. */
468
+ constructor(bufferSize: i32, none: u8[]) {
469
+ this.size = bufferSize
470
+ this.recvBuf = none
471
+ this.recvHave = none
472
+ this.sendBuf = none
473
+ this.sendAcked = none
267
474
  }
268
475
 
269
476
  /** The buffer index of stream offset `offset`. */
270
477
  at(offset: i64): i32 {
271
- const size: i32 = toI32(this.recvBuf.length)
272
- return size > 0 ? toI32(offset % toI64(size)) : 0
478
+ return this.size > 0 ? toI32(offset % toI64(this.size)) : 0
273
479
  }
274
480
 
275
481
  /** How many bytes can be read now. */
@@ -279,7 +485,7 @@ export class QuicStream {
279
485
 
280
486
  /** How many more bytes the send buffer takes. */
281
487
  room(): i64 {
282
- return toI64(toI32(this.sendBuf.length)) - (this.sendEnd - this.sendBase)
488
+ return toI64(this.size) - (this.sendEnd - this.sendBase)
283
489
  }
284
490
 
285
491
  /** Whether the sending side has a frame to go: lost bytes, new bytes, or a FIN not yet sent. */
@@ -308,11 +514,13 @@ export class QuicStream {
308
514
  return sendDone && recvDone
309
515
  }
310
516
 
311
- /** Empties the slot for stream `id`, with `recvLimit` and `sendLimit` its first credits; the buffers' bits are cleared. */
517
+ /**
518
+ * Empties the slot for stream `id`, with `recvLimit` and `sendLimit` its
519
+ * first credits. It holds no buffers here, and a borrowed bitmap comes
520
+ * back from its pool already zeroed.
521
+ */
312
522
  open(id: i64, recvLimit: i64, sendLimit: i64): void {
313
523
  this.id = id
314
- this.recvHave.fill(toU8(0))
315
- this.sendAcked.fill(toU8(0))
316
524
  this.recvRead = 0
317
525
  this.recvContiguous = 0
318
526
  this.recvHighest = 0
@@ -390,6 +598,16 @@ export class QuicStreams {
390
598
  * each of those costs one pop, not a pass over the table.
391
599
  */
392
600
  free: i32[]
601
+ /**
602
+ * Where the streams borrow their buffers: the table's own pools, or the
603
+ * set a server shares across its connections (`share`). `null` when the
604
+ * streams have no buffers at all (`bufferSize` 0, or no slots).
605
+ */
606
+ pools: QuicStreamPools | null = null
607
+ /** The empty array a side holds while it has no buffer borrowed. */
608
+ none: u8[]
609
+ /** How many receive sides hold a buffer now: the rest are what a packet could start. */
610
+ recvLent: i32 = 0
393
611
  /** The configured limits on the client's streams at once and this side's own. */
394
612
  maxBidi: i64 = 0
395
613
  maxUni: i64 = 0
@@ -462,9 +680,10 @@ export class QuicStreams {
462
680
  */
463
681
  constructor(maxBidi: i64, maxUni: i64, localStreams: i64, bufferSize: i32, windowData: i64) {
464
682
  const count: i32 = toI32(maxBidi + maxUni + localStreams)
683
+ this.none = []
465
684
  this.slots = []
466
685
  for (let k: i32 = 0; k < count; k += 1) {
467
- this.slots.push(new QuicStream(bufferSize))
686
+ this.slots.push(new QuicStream(bufferSize, this.none))
468
687
  }
469
688
  let size: i32 = 4
470
689
  while (size < count * 2) {
@@ -481,8 +700,154 @@ export class QuicStreams {
481
700
  this.reset()
482
701
  }
483
702
 
484
- /** Frees every slot and puts every counter back, for a connection slot reused for another peer. */
703
+ /**
704
+ * Borrows the stream buffers from `pools` from now on, in place of the
705
+ * table's own: how a server's connections share one set. Call it before
706
+ * any stream has borrowed, or after `reset`. Panics when the pools' buffers
707
+ * are not `bufferSize` bytes.
708
+ */
709
+ share(pools: QuicStreamPools): void {
710
+ if (pools.data.size !== this.bufferSize) {
711
+ panic(
712
+ `QuicStreams: pools of ${pools.data.size}-byte buffers shared with streams of ${this.bufferSize} bytes`
713
+ )
714
+ }
715
+ this.giveBack()
716
+ this.pools = pools
717
+ }
718
+
719
+ /**
720
+ * The pools, made for this table alone on first use when nothing shared
721
+ * one: a table that never moves a byte makes none. `null` when the
722
+ * streams have no buffers at all. Pools of one connection make every
723
+ * buffer the table could hold at once, there and then, the footprint the
724
+ * table had when each slot owned its buffers: how many it holds turns on
725
+ * when the peer acknowledges, so a lazily grown private pool would go on
726
+ * allocating after any warm-up. A server's shared pools grow lazily.
727
+ */
728
+ ownPools(): QuicStreamPools | null {
729
+ const pools: QuicStreamPools | null = this.pools
730
+ if (pools !== null) {
731
+ return pools
732
+ }
733
+ const count: i32 = toI32(this.slots.length)
734
+ if (this.bufferSize <= 0 || count <= 0) {
735
+ return null
736
+ }
737
+ const made: QuicStreamPools = new QuicStreamPools(this.bufferSize, count, 1)
738
+ made.prime(made.data.ceiling)
739
+ made.drain(QUIC_STREAM_NO_BYTES)
740
+ this.pools = made
741
+ return made
742
+ }
743
+
744
+ /**
745
+ * Readies the pools for a packet of `bytes` bytes, before the arena
746
+ * scope its frames are handled in: one buffer for every receive side it
747
+ * could start, which is no more than the sides holding none, nor than one
748
+ * per three bytes (the smallest STREAM frame carrying a byte, but for a
749
+ * last one without a length).
750
+ *
751
+ * The peer's remaining stream credit is no tighter bound: an ACK earlier
752
+ * in the same packet can finish a stream, and `release` then raises the
753
+ * limit before a later STREAM frame opens past the old one. A bound that
754
+ * allows for every such release is one buffer per side holding none, which
755
+ * `idle` already is. Once warm the buffers a packet gives back refill the
756
+ * reserve for the next one, so this costs a handful of comparisons, not a
757
+ * take per side.
758
+ */
759
+ prime(bytes: i32): void {
760
+ const pools: QuicStreamPools | null = this.ownPools()
761
+ if (pools !== null) {
762
+ const idle: i32 = toI32(this.slots.length) - this.recvLent
763
+ const most: i32 = (bytes > 0 ? bytes : QUIC_STREAM_NO_BYTES) / 3 + 1
764
+ pools.prime(idle < most ? idle : most)
765
+ }
766
+ }
767
+
768
+ /** Gives every slot's borrowed buffers back, each with its used extent zeroed. */
769
+ giveBack(): void {
770
+ const pools: QuicStreamPools | null = this.pools
771
+ if (pools !== null) {
772
+ pools.drain(pools.hold)
773
+ }
774
+ for (const stream of this.slots) {
775
+ this.giveRecv(stream)
776
+ this.giveSend(stream)
777
+ }
778
+ if (pools !== null) {
779
+ pools.drain(QUIC_STREAM_NO_BYTES)
780
+ }
781
+ }
782
+
783
+ /**
784
+ * Lends `stream` a receive buffer and bitmap from the reserve `prime`
785
+ * filled, unless it holds them already or the streams have none. Panics
786
+ * when the reserve is empty: a packet handled without `prime`, where a
787
+ * take would allocate inside the scope.
788
+ */
789
+ borrowRecv(stream: QuicStream): void {
790
+ const pools: QuicStreamPools | null = this.pools
791
+ if (stream.recvBuf.length > 0 || this.bufferSize <= 0) {
792
+ return
793
+ }
794
+ if (pools === null) {
795
+ panic("QuicStreams: a receive buffer was wanted with none primed")
796
+ }
797
+ pools.lend(stream)
798
+ this.recvLent = this.recvLent + 1
799
+ }
800
+
801
+ /**
802
+ * Gives `stream`'s receive buffer and bitmap back to the pools, zeroing
803
+ * every byte a frame reached: below `recvHighest`, which past a whole
804
+ * buffer is all of it. Nothing when it holds none.
805
+ */
806
+ giveRecv(stream: QuicStream): void {
807
+ const pools: QuicStreamPools | null = this.pools
808
+ if (pools !== null && stream.recvBuf.length > 0) {
809
+ pools.keep(
810
+ stream.recvBuf,
811
+ stream.recvHave,
812
+ toI32(quicStreamMin(stream.recvHighest, toI64(stream.size)))
813
+ )
814
+ stream.recvBuf = this.none
815
+ stream.recvHave = this.none
816
+ this.recvLent = this.recvLent - 1
817
+ }
818
+ }
819
+
820
+ /** Borrows `stream`'s send buffer and bitmap from the pools, unless it holds them already or the streams have none. */
821
+ borrowSend(stream: QuicStream): void {
822
+ const pools: QuicStreamPools | null = this.ownPools()
823
+ if (pools !== null && stream.sendBuf.length === 0) {
824
+ pools.lendSend(stream)
825
+ }
826
+ }
827
+
828
+ /**
829
+ * Gives `stream`'s send buffer and bitmap back to the pools, zeroing
830
+ * every byte a write reached: below `sendEnd`, which past a whole buffer is
831
+ * all of it. Called only once nothing can be sent again from it — every
832
+ * byte acknowledged, or the side reset — and before a reset moves
833
+ * `sendEnd` back.
834
+ */
835
+ giveSend(stream: QuicStream): void {
836
+ const pools: QuicStreamPools | null = this.pools
837
+ if (pools !== null && stream.sendBuf.length > 0) {
838
+ pools.keep(stream.sendBuf, stream.sendAcked, toI32(quicStreamMin(stream.sendEnd, toI64(stream.size))))
839
+ stream.sendBuf = this.none
840
+ stream.sendAcked = this.none
841
+ }
842
+ }
843
+
844
+ /**
845
+ * Frees every slot and puts every counter back, for a connection slot
846
+ * reused for another peer. Every buffer a stream still held goes back to
847
+ * its pool first.
848
+ */
485
849
  reset(): void {
850
+ this.giveBack()
486
851
  for (const stream of this.slots) {
487
852
  stream.id = -1
488
853
  stream.queued = false
@@ -737,6 +1102,8 @@ export class QuicStreams {
737
1102
  return
738
1103
  }
739
1104
  const id: i64 = stream.id
1105
+ this.giveRecv(stream)
1106
+ this.giveSend(stream)
740
1107
  this.indexRemove(k, id)
741
1108
  stream.id = -1
742
1109
  this.pushFree(k)
@@ -867,6 +1234,9 @@ export class QuicStreams {
867
1234
  stream.recvState = QUIC_RECV_SIZE_KNOWN
868
1235
  }
869
1236
  const before: i64 = stream.recvContiguous
1237
+ if (length > 0 && end > stream.recvContiguous) {
1238
+ this.borrowRecv(stream)
1239
+ }
870
1240
  const size: i32 = toI32(stream.recvBuf.length)
871
1241
  for (let k2: i32 = 0; k2 < length; k2 += 1) {
872
1242
  const position: i64 = offset + toI64(k2)
@@ -919,7 +1289,8 @@ export class QuicStreams {
919
1289
  this.consume(finalSize - stream.recvRead)
920
1290
  stream.recvRead = finalSize
921
1291
  stream.recvContiguous = finalSize
922
- stream.recvHave.fill(toU8(0))
1292
+ // Nothing of a reset stream is read again (§3.2 "Reset Recvd").
1293
+ this.giveRecv(stream)
923
1294
  stream.recvState = QUIC_RECV_RESET_RECVD
924
1295
  stream.resetCode = code
925
1296
  stream.maxDataOwed = false
@@ -1068,6 +1439,9 @@ export class QuicStreams {
1068
1439
  }
1069
1440
  const room: i64 = stream.room()
1070
1441
  const n: i32 = toI64(length) < room ? length : toI32(room)
1442
+ if (n > 0) {
1443
+ this.borrowSend(stream)
1444
+ }
1071
1445
  const size: i32 = toI32(stream.sendBuf.length)
1072
1446
  for (let j: i32 = 0; j < n; j += 1) {
1073
1447
  const slot: i32 = stream.at(stream.sendEnd + toI64(j))
@@ -1094,6 +1468,8 @@ export class QuicStreams {
1094
1468
  * reset it; or another `QUIC_STREAM_ERR_*`. Reading half a buffer gives the
1095
1469
  * peer more credit. A stream's receiving side is done when a read answers
1096
1470
  * `QUIC_STREAM_END` or `QUIC_STREAM_ERR_RESET`, so read until one does.
1471
+ * After `QuicConnection.release`, a stream that still owed bytes answers
1472
+ * `QUIC_STREAM_END` and copies nothing: its buffer went back to the pool.
1097
1473
  */
1098
1474
  read(id: i64, buf: u8[], at: i32, length: i32): i32 {
1099
1475
  const k: i32 = this.slotOf(id)
@@ -1110,6 +1486,11 @@ export class QuicStreams {
1110
1486
  return QUIC_STREAM_ERR_RESET
1111
1487
  }
1112
1488
  const available: i64 = stream.readable()
1489
+ // Bytes owed with no buffer left to hold them: the table was released
1490
+ // under the reader, and nothing more will arrive, so the side is over.
1491
+ if (available > 0 && stream.recvBuf.length === 0) {
1492
+ return QUIC_STREAM_END
1493
+ }
1113
1494
  const n: i32 = toI64(length) < available ? length : toI32(available)
1114
1495
  const size: i32 = toI32(stream.recvBuf.length)
1115
1496
  for (let j: i32 = 0; j < n; j += 1) {
@@ -1123,11 +1504,13 @@ export class QuicStreams {
1123
1504
  if (n > 0) {
1124
1505
  this.consume(toI64(n))
1125
1506
  }
1507
+ // The credit follows the buffer a stream borrows, not the one it holds now.
1508
+ const window: i32 = stream.size
1126
1509
  if (
1127
1510
  stream.recvState === QUIC_RECV_RECV &&
1128
- stream.recvRead + toI64(size) - stream.recvLimit >= toI64(size / 2)
1511
+ stream.recvRead + toI64(window) - stream.recvLimit >= toI64(window / 2)
1129
1512
  ) {
1130
- stream.recvLimit = stream.recvRead + toI64(size)
1513
+ stream.recvLimit = stream.recvRead + toI64(window)
1131
1514
  stream.maxDataOwed = true
1132
1515
  this.streamOwes = true
1133
1516
  }
@@ -1136,6 +1519,13 @@ export class QuicStreams {
1136
1519
  // reader that has not seen the end yet.
1137
1520
  if (n === 0 && stream.recvState === QUIC_RECV_DATA_RECVD && stream.recvRead === stream.recvFinal) {
1138
1521
  stream.recvState = QUIC_RECV_DATA_READ
1522
+ // Outside any packet's scope, so the reserve is trimmed first and the
1523
+ // buffer always has room in it.
1524
+ const pools: QuicStreamPools | null = this.pools
1525
+ if (pools !== null) {
1526
+ pools.drain(pools.hold)
1527
+ }
1528
+ this.giveRecv(stream)
1139
1529
  this.release(k)
1140
1530
  return QUIC_STREAM_END
1141
1531
  }
@@ -1151,6 +1541,9 @@ export class QuicStreams {
1151
1541
  if (stream.sendState === QUIC_SEND_NONE || stream.sendState >= QUIC_SEND_DATA_RECVD) {
1152
1542
  return
1153
1543
  }
1544
+ // Nothing is sent again once reset (§3.1), and the used extent is the
1545
+ // one written, so the buffer goes back before `sendEnd` moves.
1546
+ this.giveSend(stream)
1154
1547
  this.writtenTotal = this.writtenTotal - (stream.sendEnd - stream.sendNext)
1155
1548
  stream.sendEnd = stream.sendNext
1156
1549
  stream.sendState = QUIC_SEND_RESET_SENT
@@ -1520,6 +1913,10 @@ export class QuicStreams {
1520
1913
  return
1521
1914
  }
1522
1915
  const stream: QuicStream = this.slots[k]
1916
+ if (stream.sendState >= QUIC_SEND_DATA_RECVD) {
1917
+ // Done or reset: its buffer may be back in the pool, and nothing is owed.
1918
+ return
1919
+ }
1523
1920
  const top: i64 = offset + toI64(length) < stream.sendEnd ? offset + toI64(length) : stream.sendEnd
1524
1921
  for (let o: i64 = offset < stream.sendBase ? stream.sendBase : offset; o < top; o += 1) {
1525
1922
  quicStreamSetBit(stream.sendAcked, stream.at(o), true)
@@ -1533,7 +1930,10 @@ export class QuicStreams {
1533
1930
  stream.finAcked = true
1534
1931
  }
1535
1932
  if (stream.sendState === QUIC_SEND_DATA_SENT && stream.finAcked && stream.sendBase === stream.sendEnd) {
1933
+ // Every byte up to the final size is acknowledged: no frame reads the
1934
+ // buffer again, so only now does it go back.
1536
1935
  stream.sendState = QUIC_SEND_DATA_RECVD
1936
+ this.giveSend(stream)
1537
1937
  this.release(k)
1538
1938
  return
1539
1939
  }