@amritk/nish-x86_64-linux 0.30.0 → 0.32.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.
package/std/README.md CHANGED
@@ -12,6 +12,7 @@ whatever program imports it, and subject to the same rules as `examples/` or
12
12
  | [`testing.ts`](./testing.ts) | a test runner: a `Suite` a program drives with straight-line assertions, printing the `PASS` / `FAIL` / `SKIP` lines the repository's own harness prints, and answering the exit code |
13
13
  | [`text.ts`](./text.ts) | the string operations a program would otherwise write inline: `splitLines`, `splitWhitespace`, `trim` and its halves, `contains`, `replaceAll`, `firstDifference` over two arrays of lines, `codePointLength`, the number of Unicode code points (what JSON Schema's `minLength` counts, where `.length` counts UTF-8 bytes natively and UTF-16 units under Node; it answers the same on both by asking which it is reading), and `indexOfAny(text, bytes, from)`, the first index at or after `from` whose byte is one of 1 to 16 ASCII `bytes`. Its walker is the meaning and what runs under Node; natively the compiler replaces the one call to it with the runtime kernel `nish_str_index_of_any`, which reads 16 or 32 bytes at a time and is variable-time ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#byte-search-indexofany-in-nishtext)). `tests/link/std_text_index_of_any*` are its programs |
14
14
  | [`math.ts`](./math.ts) | `Math.tan`, `Math.atan2` and `Math.hypot` on `f64`, ported from fdlibm (`LICENSE-fdlibm`) so that every target, wasm included, answers alike without libm. A program never imports it: a call to one of the three loads it, and the compiler copies the functions the call reaches into the calling module as `internal` ones. JavaScript's answer for every NaN, signed zero and infinity, and within one ulp elsewhere (0.83, 0.84 and 0.93 ulp measured against mpmath). `tests/cases/math_std_edges` pins the special values |
15
+ | [`sort.ts`](./sort.ts) | `xs.sort(cmp)` on any array: `sortWith`, a stable merge sort over runs of 32 sorted by insertion — one scratch array released before it returns, a pair of runs already in order copied without merging, no bounds check left in it — so its order is element for element Node's `Array.prototype.sort`. Like `math.ts` it is one the compiler knows about: a program never imports it, a `.sort(cmp)` call loads it, the checker rewrites the call to `sortWith`, and each comparator's instantiation — the comparator called directly, no function pointer — is copied into the module that sorts. Under Node the call stays the platform's own. `tests/cases/arr_sort_cmp*` are its programs, a 10,000-element sort with ties among them |
15
16
  | [`regex.ts`](./regex.ts) | Regular expressions in JavaScript's syntax with the `u` flag's strictness, matched by a Pike VM in time linear in the input whatever the pattern; finding every match is one search per match, so `findAll` and its kin can be quadratic when a long branch is preferred to a short one that wins ([`docs/security/regex.md`](../docs/security/regex.md)). `regexCompile(pattern, flags)` answers a `Regex` or the error text; `test`, `exec` into a reused `RegexMatch`, `find`, `findAll`, `count`, `replace` and `replaceAll` with `$&`, `$1` and `$<name>`, and `split`. Flags `i`, `m`, `s` and `u`; `i` folds by Unicode's simple case folding, ASCII letters only to ASCII. `\p{…}` and `\P{…}` take Unicode 16.0's General_Category, Script, Script_Extensions and binary properties by JavaScript's names, from tables `scripts/gen-regex-unicode.mjs` generates into the module and checks against Node's `RegExp`; backreferences and lookaround are refused at compile time. Offsets are UTF-8 bytes. A search allocates nothing beyond its answer: the thread lists and capture tables are the `Regex`'s, so one `Regex` serves one thread at a time. A leading literal is found with `indexOf`, a small leading set with `indexOfAny`. `tests/link/std_regex` and `tests/link/std_regex_unicode` check it against JavaScript's own answers |
16
17
  | [`json.ts`](./json.ts) | `jsonField(object, name)`: the value of one field of one flat JSON object, which is the shape the compiler's own `--json` diagnostics have. A reader and not a parser — it answers text, answers `null` for a field that is not there, and does not validate. `JsonWriter` is the other direction: any JSON value written into a caller-owned `u8[]`, commas and colons included, byte for byte what `JSON.stringify` writes, and allocating nothing per call except an `f64`'s digits, a temporary string the call gives back before it returns (`text()` and `jsonStringifyString` build strings by design); misuse and a full buffer set a sticky negative `JSON_WRITE_*` code in `failed` rather than panicking. `setIndent(n)` and `setIndentTab()` write what `JSON.stringify(v, null, n)` or `(v, null, "\t")` writes, with no allocation for the indent, and `setStrict(true)` makes NaN and the infinities the sticky `JSON_WRITE_NON_FINITE` instead of `null`; both modes are off by default and kept by `reset`. `jsonStringifyString(s)` is the string rule answered as a string. `stringBytes(bytes, off, len)` and `keyBytes(bytes, off, len)` write a window of a byte array as `string` and `key` write a string of those bytes, allocating nothing, and refuse a window outside the array with `JSON_WRITE_BAD_WINDOW` |
17
18
  | [`json-tokenizer.ts`](./json-tokenizer.ts) | `JsonTokenizer`: a validating, streaming tokenizer for RFC 8259 JSON held in a window of a `u8[]`. `next()` answers one `JSON_TOK_*` token at a time — object and array openers and closers, keys, strings, numbers and the three literals — then `JSON_TOK_END`, or a negative `JSON_TOK_ERR_*` code that sticks in `failed`, with the byte in `errorAt`. Strings are checked as UTF-8 and a lone surrogate escape is refused; `stringInto` decodes the current string into the caller's array and `keyEquals` compares it with a name, neither allocating. `numberF64` is correctly rounded; `isInteger` and `numberI64` read an integer that fits an `i64`, and `isU64` and `numberU64` one that fits a `u64`. `isIntegral` asks whether a number's value is whole however it is written (`1.0`, `1e2`, `1e400`), JSON Schema's "integer", from the text and at any exponent. `skipValue` steps over a value. `JsonLimits` caps nesting depth, string, number and total bytes, each with its own code; `reset` reuses the tokenizer, so a reused body keeps the arena flat. Duplicate keys are reported as tokens. No input panics and no byte outside the window is read; [`docs/security/json.md`](../docs/security/json.md) is its record, and `tests/link/std_json_tokenizer` runs JSONTestSuite and a seeded fuzz |
@@ -41,6 +42,7 @@ whatever program imports it, and subject to the same rules as `examples/` or
41
42
  | [`term-rate.ts`](./term-rate.ts) | how fast the link to the terminal is, and the frame rate and detail a program keeps to under it. A `RateController` is told each frame sent (`frameSent(bytes, costNs, nowMs)`) and each acknowledgement — the answer to the DSR 5 query sent after a frame — (`ackReceived(nowMs)`), keeps running averages of the round trip, the bytes a second delivered and the cost of a frame, and answers `mayDraw(nowMs)` and a `RateDecision`: the frame interval, a resolution divisor, a backend cap and a palette, stepped down a tier when frames back up and up again when the link has room; `stats()` is the `TermStats` a program can show. Pure: no capability and no clock (`tests/link/std_term_rate`, `std_term_rate_f64`; [guide](../docs/guide/using-nish/terminal.md#how-the-frame-rate-adapts)) |
42
43
  | [`term-pixels.ts`](./term-pixels.ts) | a `Framebuffer` of `0xRRGGBB` pixels and the encoders that draw it with the backend the terminal has: `kittyFrame` (kitty graphics, one image id retransmitted each frame), `sixelFrame` (a fixed palette, optionally dithered), `drawHalfBlocks` onto a `Canvas` and `drawAscii` onto a `Screen`, each into the `PixelRect` `fitFramebuffer` letterboxes in the cells; `scaleFramebuffer` shrinks a frame by the controller's divisor. `PixelBytes` keeps the encoders' output and working arrays, so frame after frame allocates nothing. Pure (`tests/link/std_term_pixels`, `std_term_pixels_f64`; [guide](../docs/guide/using-nish/standard-library.md#nishterm-pixels)) |
43
44
  | [`term-io.ts`](./term-io.ts) | the term module that does the I/O. `runTerminal(options, state, update, render)` is a whole program loop: it opens the terminal, steps `update` at a fixed rate, calls `render` with a `Frame` (the `Screen`, the `Framebuffer`, the step `alpha`, `TermCaps` and `TermStats`) whenever the controller lets a frame go, and closes when `update` answers false or the user presses Ctrl-C. `openTerminal(options)` answers a `Terminal` for a program's own loop (`readEvents`, `present`, `stats`, `close`). Opening refuses a pipe, enters raw mode and the alternate screen, probes the terminal and turns on the kitty keyboard, mouse, paste and focus reports the options ask for, with the runtime's restore string set before each so a panic or signal leaves the terminal as it was; `present` sends the frame in the best backend the controller allows as one write and acknowledges it with DSR 5. Reaches `tty` (`tests/term-io/relay.mjs` drives it in a pseudo-terminal from `tests/run.js`'s `term-io` section; `tests/link/std_term_io_deny`; [guide](../docs/guide/using-nish/terminal.md)) |
45
+ | [`region.ts`](./region.ts) | The source behind the builtin module `nish:region`, and imported only by that name: `Region<T>`, `region`, `regionWith`, `enter`, `enterWith` and `free`. A `Region<T>` is a handle, a 64-bit integer, to memory the program frees when it says so; `region(f)` allocates what `f` builds into a new region and keeps its answer as the root, `enter(r, f)` runs `f(root)` there again and answers its scalar, the `*With` forms pass one argument through, and `free(r)` gives all of it back. A handle used after `free`, entered twice or freed while entered stops the program. The bodies are the meaning; the allocator swap is the emitter's (`src/emit-region.ts`) and what may cross a region's edge is the checker's (`src/region-check.ts`), keyed on this file ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#regions-nishregion); [guide](../docs/guide/language/regions.md)) |
44
46
  | [`secret.ts`](./secret.ts) | The source behind the builtin module `nish:secret`, and imported only by that name: `Secret<T>`, `secret`, `expose`, `exposeWith` and `wipe`. The bodies are the meaning; what makes a `Secret` one is the checker's, keyed on this file ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#secrets-nishsecret)). `wipe`'s body is written by the emitter, one volatile `llvm.memset`, and like `collections.ts` the module writes no `.ll` of its own |
45
47
  | [`threads.ts`](./threads.ts) | `parallelMapInto(src, dst, f)` and `parallelReduce(src, f, identity)`: a function over every element of an array, on as many threads as the length is worth. Its bodies are the sequential meaning, which is what runs under Node; the compiler recognises the two templates by module and name, lowers the one loop in each onto `nish_parallel_range`, holds the function to the rules that make that safe, and compiles an importing program with `--threads` ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#data-parallelism-nishthreads)). `tests/link/par_*` are its programs. `scope()`, bound by `using s = scope()`, runs unlike tasks, each on a thread of its own, and joins them at every exit of the block; `s.spawn(entry, arg, dst, at)` stores a task's answer, and `s.spawn(entry, arg)` and `s.spawn(() => ...)`, whose arrow reads the function's `const` locals, run one that answers `void` (`spawnWith` and `spawnThunk` are the methods the compiler reads them as, and under Node `spawn` itself tells them apart) ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#scoped-tasks-using-s--scope)). `scopeWithStack(bytes)` is a scope whose tasks each run on a new thread with at least `bytes` of stack, for a task that recurses deeper than a thread's default allows; under Node and without threads the size is checked and otherwise ignored (`tests/link/thread_stack_deep`). `Mutex<T>` is a lock that owns its data: the value is reachable only through the guard `using g = m.lock()` binds, which releases it at every exit of the block, so a scope's tasks can share and update one value without a data race or a deadlock ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#a-lock-that-owns-its-data-mutext); `tests/link/thread_mutex_*`, and `examples/histogram.ts` measured). `Channel<T>` carries scalars to one receiver, a task of the scope or the parent after the join, from the scope's sender tasks and from the parent before the block that spawns them, and closes when every sending task has returned ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#channels-channelt); [`docs/wp29-thread-surface.md`](../docs/wp29-thread-surface.md#44-stage-p3--channels-of-scalars) §4.4). Every body in the module is its meaning under Node, where a task runs at its `spawn` |
46
48
  | [`sync/shm.ts`](./sync/shm.ts) | `createRegion(name, bytes)`, `openRegion(name)` and `unlinkRegion(name)`: a named region of memory that several processes of one user map at once, through `runtime/runtime-shm.c`. A `SharedRegion` is read and written through checked slots, `loadI32` to `compareExchangeI64` at an ordering the call names (`RELAXED` to `SEQ_CST`), and byte windows (`read`, `write`); an offset outside the region or not a multiple of the slot's width stops the program. `waitI32` and `wake` sleep and wake on a slot across processes (Linux futex; `SHM_UNSUPPORTED` elsewhere). Native only, and it reaches `ffi` ([`docs/LANGUAGE.md`](../docs/LANGUAGE.md#shared-memory-between-processes-nishsyncshm-and-nishsyncring); [`docs/wp47-shared-memory.md`](../docs/wp47-shared-memory.md); [`docs/security/shm.md`](../docs/security/shm.md)) |
@@ -231,13 +233,13 @@ reads one, and it still takes its randomness as an argument.
231
233
  | [`net/dns.ts`](./net/dns.ts) | `nish/net/dns-resolver` on `nish:net` sockets, on the caller's loop: `DnsClient(resolver, loop, tokenBase)` opens a new UDP socket on a kernel-chosen port for each try, or a TCP connection for a TCP one, under a token of its range (`owns`), draws each query ID from `crypto.getRandomValues`, and never waits: the program's `pollWait` hands it `ready(token, events, now)`, calls `tick(now)` and waits no longer than `timeoutMs(now, own)`. `resolveHost(name, timeoutMs, port, out, ttls)` reads `/etc/resolv.conf` and `/etc/hosts` and blocks on a loop of its own, for a CLI or start-up; `dnsLookupWith` does the same with a configuration given. | A Nish DNS server on loopback: UDP, TCP, TC, CNAME, the search list, a dead server, `attempts`, the hosts file and spoofed replies (`tests/link/std_dns`); the capability report (`std_dns_caps`); a flat arena over 2,000 lookups (`std_dns_soak`) |
232
234
  | [`net/hpack.ts`](./net/hpack.ts) | HPACK, HTTP/2's header compression: `HpackEncoder` and `HpackDecoder`, one per direction of a connection, each with its `HpackTable`; and beneath them the §5.1 integer (`hpackEncodeInteger` / `hpackDecodeInteger`), the §5.2 string (`hpackEncodeString` / `hpackDecodeString`) and Appendix B's Huffman code (`HpackHuffman`, `hpackHuffmanEncode`, `hpackHuffmanDecode`, `hpackHuffmanLength`). Names and values are `u8[]`. The encoder takes the representation and the Huffman choice per field, and sends a never-indexed field as a literal even when a table holds it. The decoder answers a negative `HPACK_ERR_*` code, and stays spent, for a truncated block, an integer past 2^31 - 1, an index of zero or out of range, bad Huffman padding, EOS, a size update above the SETTINGS limit, after a field or missing when a lowered limit requires one, and for a block that would copy more than `HPACK_COPY_FACTOR` times the larger of the table's size and the block's length into and out of the table (`HPACK_ERR_COPY_BUDGET`; `tableCopied` counts those octets); a header list past the caller's limit is decoded to the end, its excess dropped, and answered with `HPACK_LIST_TOO_LARGE`, which is not fatal. A window outside its buffer, a negative size or limit, and an integer prefix outside 1 to 8 bits are the program's mistakes and panic | RFC 7541 Appendix C (C.1–C.6, with the dynamic table after every step) and every code of Appendix B |
233
235
  | [`net/qpack.ts`](./net/qpack.ts) | QPACK, HTTP/3's field compression, with a dynamic table of capacity 0: `QpackEncoder` and `QpackDecoder`, one per connection. The encoder writes a field section that starts `00 00` (Required Insert Count 0, Base 0) with `beginSection`, then one field line per `encodeField(out, name, off, len, value, off, len, neverIndexed, huffman)`: an Indexed Field Line when the 99-entry static table holds the field, else a literal with the name by static index when it can, each string Huffman-coded when that is shorter and `huffman` is set, and a never-indexed field always a literal with the N bit. The decoder answers a section's fields as windows into its own `bytes` (`nameStart`, `nameLength`, `valueStart`, `valueLength`, `neverIndexed`, `count`), its arrays reused from section to section. It answers QPACK_DECOMPRESSION_FAILED, and stays spent, for a truncated section, an integer past 2^62 − 1, a non-zero Required Insert Count, a negative Base, any dynamic-table reference, a static index past 98 and bad Huffman; a section past `maxFieldSectionSize` answers `QPACK_SECTION_TOO_LARGE`, which is not fatal. `receiveEncoderStream` accepts Set Dynamic Table Capacity 0 and answers QPACK_ENCODER_STREAM_ERROR for any other capacity, Insert or Duplicate; the encoder's `receiveDecoderStream` accepts Stream Cancellations, split anywhere, and answers QPACK_DECODER_STREAM_ERROR for a Section Acknowledgment or Insert Count Increment; `reason` names the rule. `qpackPushStreamCancellation` is the one instruction it may send. The Huffman code is `nish/net/hpack`'s. A window outside its buffer, a negative limit and a stream ID outside 0 to 2^62 − 1 panic | RFC 9204 Appendix B.1, every Appendix A entry by its index, B.2 to B.5's instructions refused, and a request matched byte for byte with pylsqpack's encoding |
234
- | [`net/http1.ts`](./net/http1.ts) | `Http1Parser`, an incremental HTTP/1.1 request parser: `feed(buf, off, len)` any slice, then `next()` answers `HTTP1_NEED_MORE`, `HTTP1_HEAD` (`method`, `target`, `minor`, lowercased `names` beside `values`, `header(name)`, `contentLength` or `chunked`, `keepAlive`, `upgrade`), `HTTP1_BODY` (a window onto its buffer, de-chunked), `HTTP1_END`, or one of three final events: `HTTP1_UPGRADE` (`upgradeBytes()` and `declineUpgrade()`), `HTTP1_CLOSED` and `HTTP1_ERROR` with the `status` to answer. It refuses with 400 every request-smuggling shape — `Content-Length` with `Transfer-Encoding`, a repeated or non-digit `Content-Length`, an obs-fold, a bare LF, whitespace before a colon, a malformed chunk size or terminator — and with 413, 414, 431, 501 and 505 what those mean. The head is also kept as spans in a buffer reused from request to request (`head`, `spans`, `methodIs`, `targetIs`, `headerIndex`, `headerIs`, `upgradeIs`), so a parser with `keepText` off allocates nothing per request; `maxChunk` caps each `HTTP1_BODY`, and `restart()` readies it for the next connection. The writer is `http1ResponseHead(status, reason, names, values, bodyLength)`, which adds the framing field itself and refuses (`null`) a CR, LF or other control character anywhere a caller's string lands, and `http1Chunk` and `http1LastChunk` for a chunked body; `http1WriteResponseHead` and `http1WriteChunk` write the same bytes into a caller's buffer at an offset, answering `HTTP1_NO_ROOM` or `HTTP1_REFUSED`, and the first takes a date's bytes to send as a `Date` field on a final response, unless the caller's fields hold one | RFC 9112 §2–§7 and §9.3, RFC 9110 §5, §6.2, §7.2 and §7.8; a corpus fed at every split point (`tests/link/net_http1`) |
235
- | [`net/http1-client.ts`](./net/http1-client.ts) | The client side of HTTP/1.1, sans-IO. The writer is `http1RequestHead(method, target, names, values, bodyLength)`, `Host` first and the framing field added for `bodyLength` (`Content-Length`, `HTTP1_CHUNKED` or `HTTP1_NO_BODY`), and `http1WriteRequestHead` into a caller's buffer, answering `HTTP1_NO_ROOM` or `HTTP1_REFUSED` with nothing written; it refuses a method that is not a token, a target that is not a form the method may send, a CR, LF or control byte in a value, not exactly one `Host`, the caller's own framing fields and a `Connection: upgrade`; `http1UpgradeRequestHead(method, target, names, values, upgrade)` writes `Upgrade` and `Connection: Upgrade` itself, with no body. `Http1ResponseParser(maxStatusLine, maxHeaderBytes, maxHeaders, maxBody, maxTrailerBytes)` is told what was asked (`expect(head, connect)`, then `expectUpgrade()` for a request that asked to switch protocols), fed any slice, and `next()` answers `HTTP1_INFORMATIONAL`, `HTTP1_HEAD` (`status`, `reason`, `minor`, lowercased `names` beside `values`, `header(name)`, `contentLength`), `HTTP1_BODY` (a window, de-chunked), `HTTP1_END` with `keepAlive()`, or the final `HTTP1_UPGRADE` (a 2xx to CONNECT or a 101 asked for, `tunnelBytes()`), `HTTP1_CLOSED` and `HTTP1_ERROR`; `finish()` reports the close that ends a body read until close, and anywhere else is `HTTP1_E_TRUNCATED`. Every smuggling shape is refused with its own `HTTP1_E_*` code, as is a 101 not asked for, more than `HTTP1_MAX_INFORMATIONAL` interim responses and a chunk-size line over `HTTP1_MAX_CHUNK_LINE`. Record: [`docs/security/http1-client.md`](../docs/security/http1-client.md) | RFC 9112 §4–§7 and §9.3, RFC 9110 §9.3.6 and §15.2; a corpus fed whole, cut at every byte and a byte at a time, every refusal and limit at its edge, and the server parser reading what the writer writes (`tests/link/net_http1_client`, `_f64`, `_feed_window`) |
236
- | [`net/http-client.ts`](./net/http-client.ts) | A blocking HTTP/1.1 client for command-line programs: `httpFetch(request, options)` looks the host up, connects, sends an `HttpRequest` (`method`, `url`, `header(name, value)`, `body`) and answers an `HttpResponse` (`status`, `reason`, `url`, `header(name)`) once its head is in, its body read through `body.read` or `body.readAll(limit)`; null on a failure. `HttpClient(options)` does the same and keeps one connection alive between fetches, sending an idempotent request once more when a kept connection turns out to be gone, and says why a fetch failed in `error` (an `HTTP_E_*` code, `httpErrorName`) and `errorText`. `HttpOptions` bounds every wait (`connectTimeoutMs`, `readTimeoutMs`, `totalTimeoutMs`) and every body (`maxBodyBytes`), follows redirects only when asked (`followRedirects`, `maxRedirects`, `crossOriginRedirects`, never https to http, credentials dropped across origins), and reads `http_proxy`, `https_proxy` and `no_proxy` when `useEnvProxy` is set (`HTTP_PROXY` ignored under CGI; https through `CONNECT`). Every byte goes through `nish/net/http1-client`. Native only. Record: [`docs/security/http-client.md`](../docs/security/http-client.md) | RFC 9110 §9.2.2 and §15.4, RFC 9112; a loopback server and proxy in the test itself: keep-alive, retries, redirects, proxies, timeouts and body caps (`tests/link/net_http_fetch`) |
236
+ | [`net/http1-codec.ts`](./net/http1-codec.ts) | `Http1Parser`, an incremental HTTP/1.1 request parser: `feed(buf, off, len)` any slice, then `next()` answers `HTTP1_NEED_MORE`, `HTTP1_HEAD` (`method`, `target`, `minor`, lowercased `names` beside `values`, `header(name)`, `contentLength` or `chunked`, `keepAlive`, `upgrade`), `HTTP1_BODY` (a window onto its buffer, de-chunked), `HTTP1_END`, or one of three final events: `HTTP1_UPGRADE` (`upgradeBytes()` and `declineUpgrade()`), `HTTP1_CLOSED` and `HTTP1_ERROR` with the `status` to answer. It refuses with 400 every request-smuggling shape — `Content-Length` with `Transfer-Encoding`, a repeated or non-digit `Content-Length`, an obs-fold, a bare LF, whitespace before a colon, a malformed chunk size or terminator — and with 413, 414, 431, 501 and 505 what those mean. The head is also kept as spans in a buffer reused from request to request (`head`, `spans`, `methodIs`, `targetIs`, `headerIndex`, `headerIs`, `upgradeIs`), so a parser with `keepText` off allocates nothing per request; `maxChunk` caps each `HTTP1_BODY`, and `restart()` readies it for the next connection. The writer is `http1ResponseHead(status, reason, names, values, bodyLength)`, which adds the framing field itself and refuses (`null`) a CR, LF or other control character anywhere a caller's string lands, and `http1Chunk` and `http1LastChunk` for a chunked body; `http1WriteResponseHead` and `http1WriteChunk` write the same bytes into a caller's buffer at an offset, answering `HTTP1_NO_ROOM` or `HTTP1_REFUSED`, and the first takes a date's bytes to send as a `Date` field on a final response, unless the caller's fields hold one | RFC 9112 §2–§7 and §9.3, RFC 9110 §5, §6.2, §7.2 and §7.8; a corpus fed at every split point (`tests/link/net_http1`) |
237
+ | [`net/http1-client-codec.ts`](./net/http1-client-codec.ts) | The client side of HTTP/1.1, sans-IO. The writer is `http1RequestHead(method, target, names, values, bodyLength)`, `Host` first and the framing field added for `bodyLength` (`Content-Length`, `HTTP1_CHUNKED` or `HTTP1_NO_BODY`), and `http1WriteRequestHead` into a caller's buffer, answering `HTTP1_NO_ROOM` or `HTTP1_REFUSED` with nothing written; it refuses a method that is not a token, a target that is not a form the method may send, a CR, LF or control byte in a value, not exactly one `Host`, the caller's own framing fields and a `Connection: upgrade`; `http1UpgradeRequestHead(method, target, names, values, upgrade)` writes `Upgrade` and `Connection: Upgrade` itself, with no body. `Http1ResponseParser(maxStatusLine, maxHeaderBytes, maxHeaders, maxBody, maxTrailerBytes)` is told what was asked (`expect(head, connect)`, then `expectUpgrade()` for a request that asked to switch protocols), fed any slice, and `next()` answers `HTTP1_INFORMATIONAL`, `HTTP1_HEAD` (`status`, `reason`, `minor`, lowercased `names` beside `values`, `header(name)`, `contentLength`), `HTTP1_BODY` (a window, de-chunked), `HTTP1_END` with `keepAlive()`, or the final `HTTP1_UPGRADE` (a 2xx to CONNECT or a 101 asked for, `tunnelBytes()`), `HTTP1_CLOSED` and `HTTP1_ERROR`; `finish()` reports the close that ends a body read until close, and anywhere else is `HTTP1_E_TRUNCATED`. Every smuggling shape is refused with its own `HTTP1_E_*` code, as is a 101 not asked for, more than `HTTP1_MAX_INFORMATIONAL` interim responses and a chunk-size line over `HTTP1_MAX_CHUNK_LINE`. Record: [`docs/security/http1-client-codec.md`](../docs/security/http1-client-codec.md) | RFC 9112 §4–§7 and §9.3, RFC 9110 §9.3.6 and §15.2; a corpus fed whole, cut at every byte and a byte at a time, every refusal and limit at its edge, and the server parser reading what the writer writes (`tests/link/net_http1_client`, `_f64`, `_feed_window`) |
238
+ | [`net/fetch.ts`](./net/fetch.ts) | A blocking HTTP/1.1 client for command-line programs: `fetchSync(request, options)` looks the host up, connects, sends an `HttpRequest` (`method`, `url`, `header(name, value)`, `body`) and answers an `HttpResponse` (`status`, `reason`, `url`, `header(name)`) once its head is in, its body read through `body.read` or `body.readAll(limit)`; null on a failure. `FetchSyncClient(options)` does the same and keeps one connection alive between fetches, sending an idempotent request once more when a kept connection turns out to be gone, and says why a fetch failed in `error` (an `HTTP_E_*` code, `httpErrorName`) and `errorText`. `HttpOptions` bounds every wait (`connectTimeoutMs`, `readTimeoutMs`, `totalTimeoutMs`) and every body (`maxBodyBytes`), follows redirects only when asked (`followRedirects`, `maxRedirects`, `crossOriginRedirects`, never https to http, credentials dropped across origins), and reads `http_proxy`, `https_proxy` and `no_proxy` when `useEnvProxy` is set (`HTTP_PROXY` ignored under CGI; https through `CONNECT`). Every byte goes through `nish/net/http1-client-codec`. Native only. Record: [`docs/security/fetch.md`](../docs/security/fetch.md) | RFC 9110 §9.2.2 and §15.4, RFC 9112; a loopback server and proxy in the test itself: keep-alive, retries, redirects, proxies, timeouts and body caps (`tests/link/net_http_fetch`) |
237
239
  | [`net/outbound.ts`](./net/outbound.ts) | The policy for fetching URLs your program did not choose, set as `HttpOptions.outbound`: `OutboundPolicy` reaches only public addresses (no loopback, private, link-local, CGNAT, metadata or other special-purpose range, IPv4-mapped, NAT64 and 6to4 forms judged as the IPv4 address they carry), checked on the resolved address the client then connects to, so DNS rebinding cannot move it; every redirect hop is checked again, the proxy is not used, and redirects, total time and body size are capped. `permit(cidr)` lets a trusted prefix through, `allowHost(name)` limits the names a URL or hop may carry (`HTTP_E_BLOCKED` before the lookup). Native only. Record: [`docs/security/outbound.md`](../docs/security/outbound.md) | RFC 6890, RFC 4291, RFC 6052, RFC 3056 and the IANA special-purpose address registries; every range at its edges and a loopback server fetched with and without a policy (`tests/link/net_outbound`) |
238
240
  | [`net/tls/ticket.ts`](./net/tls/ticket.ts) | Session tickets a server seals for itself (RFC 8446 §4.6.1): `tlsTicketSeal` and `tlsTicketOpen` seal a `TlsTicketState` (suite, issue time, lifetime, chain start, server name, resumption PSK) under ChaCha20-Poly1305 with a borrowed `Secret<u8[]>` key, as key id ‖ nonce ‖ ciphertext ‖ tag, and wipe the plaintext on every path. `TlsTicketKeyInfo`, `tlsTicketKeyDraw` and `tlsTicketKeyDue` are a key's public record, its draw and when to replace it: a key seals for 12 h of awake time and 2^30 tickets, and opens for 14 h. `TlsTicketStrikes` is the bounded strike register (§8.1) that makes a ticket single-use, failing closed when full | `tests/link/net_tls_ticket`: every bound at its instant and 1 ns past it, a flipped bit in each region, three key rotations, replay and a full register |
239
241
  | [`net/websocket.ts`](./net/websocket.ts) | `WsDecoder(expectMasked, maxMessage)`, the same `feed` and `next()` over WebSocket frames: `WS_MESSAGE` (fragments joined, text checked as UTF-8 fragment by fragment), `WS_PING`, `WS_PONG`, and the final `WS_CLOSE` and `WS_ERROR`, whose `closeCode` is the 1002, 1007 or 1009 to close with. `websocketFrame` encodes a frame with the mask key the caller supplies (`websocketWriteFrame` into a caller's buffer, `websocketFrameSize` its length), `reset()` readies a decoder for the next connection, `websocketClosePayload` a close frame's code and reason, and `websocketIsUtf8` checks a window. The handshake is `websocketRequestKey(parser)` over an `Http1Parser`'s head, `websocketAcceptKey`, `websocketKeyIsValid`, `websocketOfferedProtocols` and `websocketUpgradeResponse`, the 101. The client's side is `websocketClientKey(nonce)` (a 16-byte nonce the caller draws; the module draws no randomness), `websocketUpgradeRequest(host, target, key, protocols)` and `websocketCheckResponse(parser, key, offered)` over an `Http1ResponseParser` told `expectUpgrade()`, answering a `WsHandshake` (`error`, a `WS_HS_*` code, and `protocol`) | RFC 6455 §1.3's accept key, the §5.7 example frames byte for byte, §5.5's control-frame rules, §7.4's close codes |
240
- | [`net/http1-server.ts`](./net/http1-server.ts) | The server side of HTTP/1.1 (WP34 H1): `Http1Connection`, sans-IO, drives an `Http1Parser` — `feed`, then `next()` answers `H1_REQUEST` (the parser's head), `H1_BODY` (at most `chunkSize` bytes, either framing), `H1_END`, `H1_WRITE` (a held-back write may go again), `H1_WS_MESSAGE`, `H1_WS_CLOSE` or the final `H1_ERROR`; the program answers with `respond(status, names, values, bodyLength)`, `write` and `end`, or `acceptWebSocket(protocol)` and then `sendFrame` and `closeWebSocket`. Keep-alive and pipelining one exchange at a time, a response with a length or chunked (unframed to an HTTP/1.0 client), `Connection` and the framing decided by the server, every parser refusal answered with its status and a close, pings and closes answered on a WebSocket. Two slot pools for a program that owns its loop: `Http1Server` on plain TCP and `Http1TlsServer` on `nish/net/tls-tcp` with ALPN `http/1.1` or none (`accept`, `readable` / `writable`, `next(slot)`, `flush(slot)`, `close`, `expire(now)` for the idle timeout and the deadline for a whole head, `headTimeout`, against a client that trickles its head). Both build a slot's connection on the first accept into it rather than at start-up. `Http1Server` reads a socket until a short read rather than until it would block, takes a free slot off a stack of released slots or the next never-used one rather than scanning for one, and answers `rearm(slot, wants)`, whether the interest changed and needs a `pollModify` at all: a keep-alive request is one `recv` and one `send`. The slots borrow their output from one `Http1OutputPool` while they write and give it back once sent, so a pool's memory follows the connections the socket is holding back, not its size: an idle slot holds no output buffer. Every final response carries a `Date` from `HttpDate`, which reads `Date.now()` and formats at most once a second, shared by a pool's slots; `pin(unixMs)` fixes it for a test, and a program's own `date` field replaces it. Every cap is an `Http1Config` number fixed at start-up, and a warmed slot allocates nothing per request (`docs/security/http1.md`). Native only, as `nish:net` is | RFC 9112 §6, §7, §9; RFC 9110 §9.3.2, §15; RFC 6455 §4.2, §5.5. Nish clients over loopback against both carriers, requests cut at every byte, 2 MiB bodies each way, pipelining, a WebSocket echo, the arena flat over two hundred requests, the date formatted once a second, and every refusal (`tests/link/net_http1_server`); `curl` against its `serve` mode |
242
+ | [`net/http1-server.ts`](./net/http1-server.ts) | The server side of HTTP/1.1 (WP34 H1): `Http1Connection`, sans-IO, drives an `Http1Parser` — `feed`, then `next()` answers `H1_REQUEST` (the parser's head), `H1_BODY` (at most `chunkSize` bytes, either framing), `H1_END`, `H1_WRITE` (a held-back write may go again), `H1_WS_MESSAGE`, `H1_WS_CLOSE` or the final `H1_ERROR`; the program answers with `respond(status, names, values, bodyLength)`, `write` and `end`, or `acceptWebSocket(protocol)` and then `sendFrame` and `closeWebSocket`. Keep-alive and pipelining one exchange at a time, a response with a length or chunked (unframed to an HTTP/1.0 client), `Connection` and the framing decided by the server, every parser refusal answered with its status and a close, pings and closes answered on a WebSocket. Two slot pools for a program that owns its loop: `Http1Server` on plain TCP and `Http1TlsServer` on `nish/net/tls-tcp` with ALPN `http/1.1` or none (`accept`, `readable` / `writable`, `next(slot)`, `flush(slot)`, `close`, `expire(now)` for the idle timeout and the deadline for a whole head, `headTimeout`, against a client that trickles its head). Both build a slot's connection on the first accept into it rather than at start-up. `Http1Server` reads a socket until a short read rather than until it would block, takes a free slot off a stack of released slots or the next never-used one rather than scanning for one, and answers `rearm(slot, wants)`, whether the interest changed and needs a `pollModify` at all: a keep-alive request is one `recv` and one `send`. The slots borrow their output from one `Http1OutputPool` while they write and give it back once sent, so a pool's memory follows the connections the socket is holding back, not its size: an idle slot holds no output buffer. Every final response carries a `Date` from `HttpDate`, which reads `Date.now()` and formats at most once a second, shared by a pool's slots; `pin(unixMs)` fixes it for a test, and a program's own `date` field replaces it. Every cap is an `Http1Config` number fixed at start-up, and a warmed slot allocates nothing per request (`docs/security/http1-codec.md`). Native only, as `nish:net` is | RFC 9112 §6, §7, §9; RFC 9110 §9.3.2, §15; RFC 6455 §4.2, §5.5. Nish clients over loopback against both carriers, requests cut at every byte, 2 MiB bodies each way, pipelining, a WebSocket echo, the arena flat over two hundred requests, the date formatted once a second, and every refusal (`tests/link/net_http1_server`); `curl` against its `serve` mode |
241
243
  | [`net/url.ts`](./net/url.ts) | The request target's path and query, read in place: `urlQueryStart` finds the `?`, `UrlPathSegments` splits a path on the raw `/` and `UrlQuery` yields a query's pairs as offsets, both reusable by `reset`; `percentDecode` and `percentDecodePathSegment` decode a window into the caller's array and answer the length or a `URL_DECODE_ERR_*` code, `percentDecodeErrorAt` answers the offset an error is at, and `urlQueryFind` finds a key. Decoding is strict by default (a malformed escape and `%00` are refused); `URL_DECODE_FORM`, `URL_DECODE_LENIENT` and `URL_DECODE_ALLOW_NUL` together decode valid UTF-8 as `URLSearchParams` does. A query yields at most `UrlQueryLimits.maxPairs` pairs (100 by default) and then answers `URL_QUERY_ERR_PAIRS`. The byte API allocates nothing; `urlDecode` and `urlQueryGet` answer strings and refuse what is not UTF-8. Whole URLs: `urlParse` splits an absolute URL into a `Url` (`scheme`, `hasAuthority`, `username`, `password`, `host`, `port`, `path`, `query`, `fragment`, each still percent-encoded, the scheme and host lower-cased), `urlResolve(base, ref)` resolves a reference against one, `urlSerialize` writes one back, and `urlDefaultPort` and `urlOrigin` answer the port and the origin a client connects to. Both refuse with null what two readers could take for different hosts: a byte outside printable ASCII, a backslash, a malformed `%`, a second `@`, a non-unreserved host byte, a numeric host that is not strict dotted-decimal IPv4, an IPv6 zone, a port past 65535. Records: [`docs/security/url.md`](../docs/security/url.md) | RFC 3986 §2.1 and §3.3, the WHATWG URL Standard's percent-decode and `application/x-www-form-urlencoded` parser; `decodeURIComponent` and `URLSearchParams` on 3,000 seeded inputs; RFC 3986 §3 and §5.2, every §5.4 example, 57 refusals and 4,000 seeded round trips in both number modes (`tests/link/net_url_parse`) |
242
244
  | [`net/http-fields.ts`](./net/http-fields.ts) | The header model HTTP/2 and HTTP/3 share, version-neutral: `HttpFields` reads a decoded section with `readRequest(names, values, extendedConnect)`, `readResponse` or `readTrailers` into `method`, `scheme`, `authority`, `path`, `protocol`, `status`, the regular fields in order (`get(name)`) and `contentLength`, answering `HTTP_FIELDS_OK` or the first refusal: a name that is empty, not a token or not lowercase; a value with NUL, CR or LF or whitespace at an end; a connection-specific field (`te` but `trailers` included); a pseudo-header unknown, repeated, after a regular field, forbidden by the shape or missing; a pseudo-header value of the wrong syntax; a `host` that disagrees with `:authority`; a bad or disagreeing `content-length`. `:protocol` (RFC 8441) is read only where the caller enabled extended CONNECT. `httpFieldNameValid`, `httpFieldValueValid`, `httpFieldConnectionSpecific` and `httpFieldsCheckOutgoing` check one name, one value or a section a program sends. `httpDateWrite(out, at, seconds)` writes a second since 1970 as the IMF-fixdate a `Date` field holds (`HTTP_DATE_SIZE` bytes, to 9999), reading no clock, so it compiles for wasm32 | RFC 9110 §5, RFC 9113 §8.2–§8.5, RFC 9114 §4.2–§4.3, RFC 8441 §4; every refusal in `tests/link/net_http_fields`; RFC 9110 §5.6.7, every day from 1970 to 2399 (`tests/link/net_http_date`) |
243
245
  | [`net/http2-frame.ts`](./net/http2-frame.ts) | HTTP/2's frames, sans-IO and allocation-free: `http2ReadHeader` and `http2ParseFrame` read any frame of RFC 9113 §6 into a reused `Http2Frame` (its content left in the buffer as a window), answering 0 or the connection error — FRAME_SIZE_ERROR past the reader's SETTINGS_MAX_FRAME_SIZE or for a frame of the wrong length, PROTOCOL_ERROR for a frame on the wrong stream, padding as long as its frame, or a WINDOW_UPDATE of zero on the connection — with a stream error (`streamError`) for a PRIORITY of the wrong length, a self-dependency or a WINDOW_UPDATE of zero on a stream. `http2SettingCount`, `http2SettingId`, `http2SettingValue` and `http2SettingError` read and check SETTINGS. The writers, one per type (`http2WriteData` … `http2WriteContinuation`, `http2WriteSettingsAck`), write into the caller's buffer at an offset and answer -1 when a frame does not fit | Every type against frames built by hand from §4.1 and §6, both directions, and every refusal (`tests/link/net_http2_frame`) |
@@ -251,7 +253,7 @@ reads one, and it still takes its randomness as an argument.
251
253
  | [`net/router.ts`](./net/router.ts) | `Router`, a method and path router that knows no carrier and allocates nothing per match: `add(method, pattern)` answers the route's index (0, 1, 2 in the order added) or a refusal — `ROUTER_BAD_PATTERN`, `ROUTER_BAD_METHOD`, `ROUTER_TOO_LONG`, `ROUTER_AMBIGUOUS` (`*` not last), `ROUTER_DUPLICATE` (the same shape and method, whatever the parameter names) or `ROUTER_FULL`, against `RouterLimits`; `match(method, mStart, mEnd, target, tStart, tEnd, params)` answers the index, `ROUTER_NOT_FOUND` or `ROUTER_METHOD_NOT_ALLOWED`, writing each `:name` and a final `*` as a `[start, end)` pair into the caller's `params` (`ROUTER_TOO_MANY_PARAMS` when it is short). Literal before parameter before `*`, segment by segment, backtracking; a lookup enters each node at most once. The query is left unparsed and nothing is decoded: `routerDecode` decodes one parameter (`ROUTER_BAD_ESCAPE`, `ROUTER_NO_ROOM`) (`docs/security/router.md`) | Every refusal code, precedence and backtracking, 405 against 404, `%2F` kept in one segment, the query, `*` and authority-form targets, a target deeper than any route, the lookup bound, params past storage, and the arena flat over matches (`tests/link/net_router`, `net_router_f64`, five `net_router_negative_*`) |
252
254
  | [`net/handler.ts`](./net/handler.ts) | `HttpHandler`, complete requests and their answers over the HTTP/1.1 carrier: it owns the readiness loop, routes each request through a `Router` at its head, gathers its body up to `HandlerLimits.http.maxBody` (1 MiB; past it the carrier answers 413), and hands it over whole. The program pulls — `wait(ms)`, `ready()`, `take()` — and switches on `req.route`; `HttpRequest` reads parameters, form-decoded query values and fields as ranges into its own buffers. Each request is answered once, now or in a later turn, by `send`, `sendFields`, `empty`, `json()` + `sendJson`, `problem` (RFC 9457), `refuse` (404, or 405 with `Allow`) or `start`/`write`/`end`; an answer whose connection closed first is `HANDLER_DROPPED` and reaches no later peer. `HEAD` takes a `GET` route. `watch`/`fired` add the program's own descriptors (`signalFd()`), and `drain` stops gracefully. Nothing is allocated per request once warm (`docs/security/handler.md`) | Routing, parameters and form-decoded queries, `HEAD`, 404 and 405 problems, chunked bodies in and out, the body cap at and one past it, pipelining order, late answers dropped across a reused slot, the arena and pool flat over two thousand requests, and the drain (`tests/link/net_handler`, and `net_http_app` over `examples/http-app`) |
253
255
  | [`net/upstream.ts`](./net/upstream.ts) | `Upstream`, outbound HTTP/1.1 calls from a route handler that never block it: a pool of keep-alive connections to one `http:` origin on the `HttpHandler`'s own loop (watch ids `UpstreamLimits.firstId` and up). `get(req, path)` returns at once, or `UPSTREAM_E_FULL` with nothing held; each call comes back once through `ready()`/`take()` tagged with `r.req`, `ok` with its status and `body[0 .. length)`, or failed with the codec's `HTTP1_E_` refusal or an `UPSTREAM_E_` code (timeout, closed, connect, origin, lookup, blocked, request), and goes back with `release`. The origin is looked up once at start-up and pinned; `UpstreamLimits.outbound` checks it against an `OutboundPolicy`. A failed call's connection is closed. Each connection keeps one codec in spans mode, so a warm pool allocates nothing per call | A slow upstream while other clients and a second upstream are answered, the timeout, keep-alive and tagging, five codec refusals as failures, the policy refusing loopback, origin and request-target refusals, three thousand warm calls on one connection with `Arena.used()` flat (`tests/link/net_upstream`), and a second `release` panicking (`net_upstream_release`) |
254
- | [`net/postgres.ts`](./net/postgres.ts) | `PgConnection`, a PostgreSQL client that reads no socket: the program fills `inputBuffer()` and sends `outputBuffer()`, and `next()` answers `PG_NEED_MORE`, `PG_READY`, `PG_DESCRIBED`, `PG_ROW`, `PG_COMPLETE`, `PG_ERROR`, `PG_NOTICE`, `PG_GROUP_DONE` or the final `PG_FAILED` (`failure`, one of the `PG_ERR_*` codes). Protocol 3.0: startup, `trust` and SCRAM-SHA-256 with the server's signature checked (cleartext and MD5 refused), the extended query protocol — `prepare`, `bindStart`, `paramInt` / `paramText` / `paramBytes` / `paramTextArray` / `paramIntArray` / `paramNull`, an array built element by element into the Bind (`arrayStart`, `arrayElement`, `arrayInt`, `arrayNull`, `arrayEnd`), `bindEnd`, `bindStartBinary` for a Bind whose result columns come in binary (read in place by `binaryInt`, `binaryBool`, `binaryFloat` and `PgBinary`'s `timestamp` and `iso`, and a one-dimensional `int2[]`, `int4[]` or `int8[]` by `PgBinaryArray`), `bindCancel` to drop a Bind not yet ended, `closeStatement`, `sync` — with up to `maxInFlight` groups pipelined. `PgBatcher` queues keyed reads of one statement shape (`addInt`, `addText`) and `take(conn)` binds up to `maxRequests` of them, each key once, as one execution over `unnest($1) with ordinality`; `row`, `end` and `fail` route its rows by their server-supplied ordinal, checked first (read with `binaryInt` when `PgBatchConfig.binary` is set, so the batch's rows come in binary too), to `next(run)`'s per-request `PG_BATCH_ROW` and `PG_BATCH_DONE` / `PG_BATCH_MISSING` / `PG_BATCH_FAILED`; `take` answers a fresh handle each time, so a server's late or repeated event on a spent one is counted in `stray`, never routed or panicked on. It reads no clock and allocates nothing per request or row. `PgBatchClock` says when to take, from times the program passes in: once max(`minCount`, the moving average of batch sizes × `factor`) wait, the oldest has waited `maxWaitUs`, or, with the idle bypass, nothing is in flight and the load is light (its defaults are the fixed window); `PgBatcher.takeDue` takes only then, and `PgBatchTurns` lets one free connection collect while the others execute. A row's columns are windows onto the receive buffer (`colStart`, `colLength`), read in place by `int`, `bool`, `isNull` and `PgText`'s decoders (`timestamptz`, `PgArrayWalker` for `text[]`); no per-row or per-column allocation. The password is a `Secret<u8[]>`, wiped once the proof is made. Caps on message size, columns, groups in flight and SCRAM iterations (`docs/security/postgres.md`) | PostgreSQL 18's frontend/backend protocol chapter, RFC 5802 and RFC 7677: every builder, event and refusal (`tests/link/std_postgres`), and exchanges recorded from PostgreSQL 18 by `tests/postgres/record.mjs` replayed byte for byte (`tests/link/std_postgres_replay`); the batcher's queueing, caps and ordinal refusals (`tests/link/std_postgres_batch`); the clock's reasons, backlog and turns (`tests/link/std_postgres_batch_clock`) |
256
+ | [`net/postgres.ts`](./net/postgres.ts) | `PgConnection`, a PostgreSQL client that reads no socket: the program fills `inputBuffer()` and sends `outputBuffer()`, and `next()` answers `PG_NEED_MORE`, `PG_READY`, `PG_DESCRIBED`, `PG_ROW`, `PG_COMPLETE`, `PG_ERROR`, `PG_NOTICE`, `PG_GROUP_DONE` or the final `PG_FAILED` (`failure`, one of the `PG_ERR_*` codes). Protocol 3.0: startup, `trust` and SCRAM-SHA-256 with the server's signature checked (cleartext and MD5 refused), the extended query protocol — `prepare`, `bindStart`, `paramInt` / `paramText` / `paramBytes` / `paramTextArray` / `paramIntArray` / `paramNull`, an array built element by element into the Bind (`arrayStart`, `arrayElement`, `arrayInt`, `arrayNull`, `arrayEnd`), binary `int4[]` / `int8[]` parameters (`paramInt4Array` / `paramInt8Array` over an `i64[]` window, or `binaryArrayStart`, `binaryArrayInt`, `binaryArrayNull` and the same `arrayEnd`; per-parameter format codes only once a binary one is bound; an `int4` element out of range panics), `bindEnd`, `bindStartBinary` for a Bind whose result columns come in binary (read in place by `binaryInt`, `binaryBool`, `binaryFloat` and `PgBinary`'s `timestamp` and `iso`, and a one-dimensional `int2[]`, `int4[]` or `int8[]` by `PgBinaryArray`), `bindCancel` to drop a Bind not yet ended, `closeStatement`, `sync` — with up to `maxInFlight` groups pipelined. `PgBatcher` queues keyed reads of one statement shape (`addInt`, `addText`) and `take(conn)` binds up to `maxRequests` of them, each key once, as one execution over `unnest($1) with ordinality`; `row`, `end` and `fail` route its rows by their server-supplied ordinal, checked first (read with `binaryInt` when `PgBatchConfig.binary` is set, so the batch's rows come in binary too), to `next(run)`'s per-request `PG_BATCH_ROW` and `PG_BATCH_DONE` / `PG_BATCH_MISSING` / `PG_BATCH_FAILED`; `take` answers a fresh handle each time, so a server's late or repeated event on a spent one is counted in `stray`, never routed or panicked on. It reads no clock and allocates nothing per request or row. `PgBatchClock` says when to take, from times the program passes in: once max(`minCount`, the moving average of batch sizes × `factor`) wait, the oldest has waited `maxWaitUs`, or, with the idle bypass, nothing is in flight and the load is light (its defaults are the fixed window); `PgBatcher.takeDue` takes only then, and `PgBatchTurns` lets one free connection collect while the others execute. A row's columns are windows onto the receive buffer (`colStart`, `colLength`), read in place by `int`, `bool`, `isNull` and `PgText`'s decoders (`timestamptz`, `PgArrayWalker` for `text[]`); no per-row or per-column allocation. The password is a `Secret<u8[]>`, wiped once the proof is made. Caps on message size, columns, groups in flight and SCRAM iterations (`docs/security/postgres.md`) | PostgreSQL 18's frontend/backend protocol chapter, RFC 5802 and RFC 7677: every builder, event and refusal (`tests/link/std_postgres`), and exchanges recorded from PostgreSQL 18 by `tests/postgres/record.mjs` replayed byte for byte (`tests/link/std_postgres_replay`); the batcher's queueing, caps and ordinal refusals (`tests/link/std_postgres_batch`); the clock's reasons, backlog and turns (`tests/link/std_postgres_batch_clock`) |
255
257
  | [`net/postgres-carrier.ts`](./net/postgres-carrier.ts) | `PgCarrier`, a `PgPool`'s queries on an `HttpHandler`'s loop: its sockets sit in the handler's epoll set, a route handler hands a request to `run` (or `runInt` / `runText` for one of up to 8 attached `PgBatcher`s) and gets its number at once, and `next()` answers `PG_CARRIER_BUILD` to bind a plain query on `connection()`, `PG_CARRIER_ROW`, `PG_CARRIER_COMPLETE`, `PG_CARRIER_ERROR` and one final `PG_CARRIER_DONE`, `PG_CARRIER_MISSING` or `PG_CARRIER_FAILED` (`failure`, `socketError`) with `request()` the request to answer and `tag` the caller's. It connects the pool's slots itself (TCP or a Unix socket), times out a connect after `connectTimeoutMs`, sheds batched keys when no slot can connect, and sizes every table from `PgCarrierLimits` at construction: `run` answers `PG_CARRIER_FULL` rather than grow one, and once connections are up it allocates nothing per query. Kept apart from `net/postgres.ts` so that module reaches no capability (`docs/security/postgres.md`) | A loopback backend in the test itself: routing, a slow query not stalling another client, lost and refused connections, a connect timeout, and `Arena.used()` flat over thousands of queries (`tests/link/std_postgres_carrier`); a live server by `tests/postgres/live/run.mjs` |
256
258
  | [`net/prefork.ts`](./net/prefork.ts) | A server on every core: `preforkWorkers(process.argv)` or `preforkWith(process.argv, config)` answers a worker's index in each worker it starts again from the same program, and in the supervisor runs them until SIGTERM or SIGINT, restarting one that exits after a backoff. `PreforkConfig.supervisorChannels` gives each worker a `unixSocketPair` to the supervisor, which `preforkSupervise(args, config, state, onChannel)` hands to a callback; `serviceWorker` gives every other worker a pair to that one, and makes the workers one group that restarts together on fresh pairs. The ends arrive from descriptor 3 through `spawnWithFds`, found with `preforkSupervisorChannel`, `preforkServiceChannel` and `preforkServiceChannelOf`. Native only | `tests/link/net_prefork` (the spread, a dead worker, SIGTERM, the backoff); `tests/link/net_prefork_channels` (each worker holds exactly its own ends, a restart, the supervisor's descriptors flat) |
257
259
 
@@ -347,12 +349,12 @@ send, over loopback from a Nish UDP client, exchanges recorded against
347
349
  aioquic by their `record.sh`: a handshake and echo, and Version Negotiation,
348
350
  Retry, key update both ways, the idle timeout and a stateless reset.
349
351
 
350
- `net/http1.ts`, `net/http1-server.ts` and `net/websocket.ts` run their checks
352
+ `net/http1-codec.ts`, `net/http1-server.ts` and `net/websocket.ts` run their checks
351
353
  again under `--number-mode f64` (`net_http1_f64`, `net_http1_server_f64`,
352
354
  `net_websocket_f64`). So does the HTTP date (`net_http_date_f64`). None
353
355
  handles a secret but through `nish/net/tls-tcp`, so none is in the
354
356
  constant-time check; their record is
355
- [`docs/security/http1.md`](../docs/security/http1.md), written with the code
357
+ [`docs/security/http1-codec.md`](../docs/security/http1-codec.md), written with the code
356
358
  rather than after an audit.
357
359
 
358
360
  ## How a program imports it
@@ -1024,7 +1024,9 @@ const bcryptLoadSlot = (state: Secret<u32[]>, password: Secret<u8[]>, slot: i32)
1024
1024
  // the lane count changed (bench/crypto/bcrypt/gen-body.mjs writes them), not
1025
1025
  // one loop over a lane count: the lanes' l and r must live in registers and
1026
1026
  // their loads be scheduled together, which a loop over an array of lanes
1027
- // does not give.
1027
+ // does not give. Two lanes take each byte by its own shift and write every
1028
+ // lane's bytes before any lane's update, the form all widths had before the
1029
+ // rotated copy, which measured slower there.
1028
1030
 
1029
1031
  /** `bcryptBody4`'s interleave on two lanes. */
1030
1032
  const bcryptBody2 = (st: u32[]): void => {
@@ -1089,35 +1091,41 @@ const bcryptBody3 = (st: u32[]): void => {
1089
1091
  l1 ^= st[1042]
1090
1092
  l2 ^= st[2084]
1091
1093
  for (let n: i32 = 1; n < 17; n += 2) {
1094
+ const l0e: u32 = ctSelect((l0 >>> 8) | (l0 << 24), 0xffffffff, 0)
1092
1095
  const l0a: i32 = toI32(l0 >>> 24) & 0xff
1093
- const l0b: i32 = toI32(l0 >>> 16) & 0xff
1094
- const l0c: i32 = toI32(l0 >>> 8) & 0xff
1096
+ const l0b: i32 = toI32(l0e >>> 8) & 0xff
1097
+ const l0c: i32 = toI32(l0e) & 0xff
1095
1098
  const l0d: i32 = toI32(l0) & 0xff
1099
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1100
+ const l1e: u32 = ctSelect((l1 >>> 8) | (l1 << 24), 0xffffffff, 0)
1096
1101
  const l1a: i32 = toI32(l1 >>> 24) & 0xff
1097
- const l1b: i32 = toI32(l1 >>> 16) & 0xff
1098
- const l1c: i32 = toI32(l1 >>> 8) & 0xff
1102
+ const l1b: i32 = toI32(l1e >>> 8) & 0xff
1103
+ const l1c: i32 = toI32(l1e) & 0xff
1099
1104
  const l1d: i32 = toI32(l1) & 0xff
1105
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1106
+ const l2e: u32 = ctSelect((l2 >>> 8) | (l2 << 24), 0xffffffff, 0)
1100
1107
  const l2a: i32 = toI32(l2 >>> 24) & 0xff
1101
- const l2b: i32 = toI32(l2 >>> 16) & 0xff
1102
- const l2c: i32 = toI32(l2 >>> 8) & 0xff
1108
+ const l2b: i32 = toI32(l2e >>> 8) & 0xff
1109
+ const l2c: i32 = toI32(l2e) & 0xff
1103
1110
  const l2d: i32 = toI32(l2) & 0xff
1104
- r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1105
- r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1106
1111
  r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1112
+ const r0e: u32 = ctSelect((r0 >>> 8) | (r0 << 24), 0xffffffff, 0)
1107
1113
  const r0a: i32 = toI32(r0 >>> 24) & 0xff
1108
- const r0b: i32 = toI32(r0 >>> 16) & 0xff
1109
- const r0c: i32 = toI32(r0 >>> 8) & 0xff
1114
+ const r0b: i32 = toI32(r0e >>> 8) & 0xff
1115
+ const r0c: i32 = toI32(r0e) & 0xff
1110
1116
  const r0d: i32 = toI32(r0) & 0xff
1117
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1118
+ const r1e: u32 = ctSelect((r1 >>> 8) | (r1 << 24), 0xffffffff, 0)
1111
1119
  const r1a: i32 = toI32(r1 >>> 24) & 0xff
1112
- const r1b: i32 = toI32(r1 >>> 16) & 0xff
1113
- const r1c: i32 = toI32(r1 >>> 8) & 0xff
1120
+ const r1b: i32 = toI32(r1e >>> 8) & 0xff
1121
+ const r1c: i32 = toI32(r1e) & 0xff
1114
1122
  const r1d: i32 = toI32(r1) & 0xff
1123
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1124
+ const r2e: u32 = ctSelect((r2 >>> 8) | (r2 << 24), 0xffffffff, 0)
1115
1125
  const r2a: i32 = toI32(r2 >>> 24) & 0xff
1116
- const r2b: i32 = toI32(r2 >>> 16) & 0xff
1117
- const r2c: i32 = toI32(r2 >>> 8) & 0xff
1126
+ const r2b: i32 = toI32(r2e >>> 8) & 0xff
1127
+ const r2c: i32 = toI32(r2e) & 0xff
1118
1128
  const r2d: i32 = toI32(r2) & 0xff
1119
- l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1120
- l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1121
1129
  l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1122
1130
  }
1123
1131
  const t0: u32 = r0
@@ -1143,6 +1151,19 @@ const bcryptBody3 = (st: u32[]): void => {
1143
1151
  * place, the four encryption chains interleaved round by round so that one
1144
1152
  * lane's S-box loads overlap the others' instead of waiting on their own. The
1145
1153
  * hot loop of the batch: 2^(cost+1) calls of 521 blocks per lane.
1154
+ *
1155
+ * Each lane's half-round is written whole, its bytes and then its update,
1156
+ * before the next lane's. The two middle index bytes come from one copy of the
1157
+ * word rotated right by 8 (`e`: byte 1 is its low byte, byte 2 its second),
1158
+ * behind the value barrier: `ctSelect(v, 0xffffffff, 0)` is `v`, opaque, so
1159
+ * LLVM cannot fold the two bytes back into two shifts of the word, and copies
1160
+ * it once instead of twice; on a BMI2 target the copy is one `rorx`. Three
1161
+ * and five lanes rotate the copy the same way; six shift it right by 8, which
1162
+ * has the same two low bytes and measured faster there. On a
1163
+ * Xeon Platinum 8260 that is 6% less time a hash at the best width, 9% under
1164
+ * `--cpu x86-64-v3` (docs/security/crypto-bcrypt.md, "Cascade Lake"). The
1165
+ * barrier is an identity on a register: it adds no branch and no load, and
1166
+ * the round reads the same four S-box words.
1146
1167
  */
1147
1168
  const bcryptBody4 = (st: u32[]): void => {
1148
1169
  if (st.length < 4168) {
@@ -1162,45 +1183,53 @@ const bcryptBody4 = (st: u32[]): void => {
1162
1183
  l2 ^= st[2084]
1163
1184
  l3 ^= st[3126]
1164
1185
  for (let n: i32 = 1; n < 17; n += 2) {
1186
+ const l0e: u32 = ctSelect((l0 >>> 8) | (l0 << 24), 0xffffffff, 0)
1165
1187
  const l0a: i32 = toI32(l0 >>> 24) & 0xff
1166
- const l0b: i32 = toI32(l0 >>> 16) & 0xff
1167
- const l0c: i32 = toI32(l0 >>> 8) & 0xff
1188
+ const l0b: i32 = toI32(l0e >>> 8) & 0xff
1189
+ const l0c: i32 = toI32(l0e) & 0xff
1168
1190
  const l0d: i32 = toI32(l0) & 0xff
1191
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1192
+ const l1e: u32 = ctSelect((l1 >>> 8) | (l1 << 24), 0xffffffff, 0)
1169
1193
  const l1a: i32 = toI32(l1 >>> 24) & 0xff
1170
- const l1b: i32 = toI32(l1 >>> 16) & 0xff
1171
- const l1c: i32 = toI32(l1 >>> 8) & 0xff
1194
+ const l1b: i32 = toI32(l1e >>> 8) & 0xff
1195
+ const l1c: i32 = toI32(l1e) & 0xff
1172
1196
  const l1d: i32 = toI32(l1) & 0xff
1197
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1198
+ const l2e: u32 = ctSelect((l2 >>> 8) | (l2 << 24), 0xffffffff, 0)
1173
1199
  const l2a: i32 = toI32(l2 >>> 24) & 0xff
1174
- const l2b: i32 = toI32(l2 >>> 16) & 0xff
1175
- const l2c: i32 = toI32(l2 >>> 8) & 0xff
1200
+ const l2b: i32 = toI32(l2e >>> 8) & 0xff
1201
+ const l2c: i32 = toI32(l2e) & 0xff
1176
1202
  const l2d: i32 = toI32(l2) & 0xff
1203
+ r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1204
+ const l3e: u32 = ctSelect((l3 >>> 8) | (l3 << 24), 0xffffffff, 0)
1177
1205
  const l3a: i32 = toI32(l3 >>> 24) & 0xff
1178
- const l3b: i32 = toI32(l3 >>> 16) & 0xff
1179
- const l3c: i32 = toI32(l3 >>> 8) & 0xff
1206
+ const l3b: i32 = toI32(l3e >>> 8) & 0xff
1207
+ const l3c: i32 = toI32(l3e) & 0xff
1180
1208
  const l3d: i32 = toI32(l3) & 0xff
1181
- r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1182
- r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1183
- r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1184
1209
  r3 ^= st[3126 + n] ^ (((st[3144 + l3a] + st[3400 + l3b]) ^ st[3656 + l3c]) + st[3912 + l3d])
1210
+ const r0e: u32 = ctSelect((r0 >>> 8) | (r0 << 24), 0xffffffff, 0)
1185
1211
  const r0a: i32 = toI32(r0 >>> 24) & 0xff
1186
- const r0b: i32 = toI32(r0 >>> 16) & 0xff
1187
- const r0c: i32 = toI32(r0 >>> 8) & 0xff
1212
+ const r0b: i32 = toI32(r0e >>> 8) & 0xff
1213
+ const r0c: i32 = toI32(r0e) & 0xff
1188
1214
  const r0d: i32 = toI32(r0) & 0xff
1215
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1216
+ const r1e: u32 = ctSelect((r1 >>> 8) | (r1 << 24), 0xffffffff, 0)
1189
1217
  const r1a: i32 = toI32(r1 >>> 24) & 0xff
1190
- const r1b: i32 = toI32(r1 >>> 16) & 0xff
1191
- const r1c: i32 = toI32(r1 >>> 8) & 0xff
1218
+ const r1b: i32 = toI32(r1e >>> 8) & 0xff
1219
+ const r1c: i32 = toI32(r1e) & 0xff
1192
1220
  const r1d: i32 = toI32(r1) & 0xff
1221
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1222
+ const r2e: u32 = ctSelect((r2 >>> 8) | (r2 << 24), 0xffffffff, 0)
1193
1223
  const r2a: i32 = toI32(r2 >>> 24) & 0xff
1194
- const r2b: i32 = toI32(r2 >>> 16) & 0xff
1195
- const r2c: i32 = toI32(r2 >>> 8) & 0xff
1224
+ const r2b: i32 = toI32(r2e >>> 8) & 0xff
1225
+ const r2c: i32 = toI32(r2e) & 0xff
1196
1226
  const r2d: i32 = toI32(r2) & 0xff
1227
+ l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1228
+ const r3e: u32 = ctSelect((r3 >>> 8) | (r3 << 24), 0xffffffff, 0)
1197
1229
  const r3a: i32 = toI32(r3 >>> 24) & 0xff
1198
- const r3b: i32 = toI32(r3 >>> 16) & 0xff
1199
- const r3c: i32 = toI32(r3 >>> 8) & 0xff
1230
+ const r3b: i32 = toI32(r3e >>> 8) & 0xff
1231
+ const r3c: i32 = toI32(r3e) & 0xff
1200
1232
  const r3d: i32 = toI32(r3) & 0xff
1201
- l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1202
- l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1203
- l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1204
1233
  l3 ^= st[3126 + n + 1] ^ (((st[3144 + r3a] + st[3400 + r3b]) ^ st[3656 + r3c]) + st[3912 + r3d])
1205
1234
  }
1206
1235
  const t0: u32 = r0
@@ -1248,55 +1277,65 @@ const bcryptBody5 = (st: u32[]): void => {
1248
1277
  l3 ^= st[3126]
1249
1278
  l4 ^= st[4168]
1250
1279
  for (let n: i32 = 1; n < 17; n += 2) {
1280
+ const l0e: u32 = ctSelect((l0 >>> 8) | (l0 << 24), 0xffffffff, 0)
1251
1281
  const l0a: i32 = toI32(l0 >>> 24) & 0xff
1252
- const l0b: i32 = toI32(l0 >>> 16) & 0xff
1253
- const l0c: i32 = toI32(l0 >>> 8) & 0xff
1282
+ const l0b: i32 = toI32(l0e >>> 8) & 0xff
1283
+ const l0c: i32 = toI32(l0e) & 0xff
1254
1284
  const l0d: i32 = toI32(l0) & 0xff
1285
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1286
+ const l1e: u32 = ctSelect((l1 >>> 8) | (l1 << 24), 0xffffffff, 0)
1255
1287
  const l1a: i32 = toI32(l1 >>> 24) & 0xff
1256
- const l1b: i32 = toI32(l1 >>> 16) & 0xff
1257
- const l1c: i32 = toI32(l1 >>> 8) & 0xff
1288
+ const l1b: i32 = toI32(l1e >>> 8) & 0xff
1289
+ const l1c: i32 = toI32(l1e) & 0xff
1258
1290
  const l1d: i32 = toI32(l1) & 0xff
1291
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1292
+ const l2e: u32 = ctSelect((l2 >>> 8) | (l2 << 24), 0xffffffff, 0)
1259
1293
  const l2a: i32 = toI32(l2 >>> 24) & 0xff
1260
- const l2b: i32 = toI32(l2 >>> 16) & 0xff
1261
- const l2c: i32 = toI32(l2 >>> 8) & 0xff
1294
+ const l2b: i32 = toI32(l2e >>> 8) & 0xff
1295
+ const l2c: i32 = toI32(l2e) & 0xff
1262
1296
  const l2d: i32 = toI32(l2) & 0xff
1297
+ r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1298
+ const l3e: u32 = ctSelect((l3 >>> 8) | (l3 << 24), 0xffffffff, 0)
1263
1299
  const l3a: i32 = toI32(l3 >>> 24) & 0xff
1264
- const l3b: i32 = toI32(l3 >>> 16) & 0xff
1265
- const l3c: i32 = toI32(l3 >>> 8) & 0xff
1300
+ const l3b: i32 = toI32(l3e >>> 8) & 0xff
1301
+ const l3c: i32 = toI32(l3e) & 0xff
1266
1302
  const l3d: i32 = toI32(l3) & 0xff
1303
+ r3 ^= st[3126 + n] ^ (((st[3144 + l3a] + st[3400 + l3b]) ^ st[3656 + l3c]) + st[3912 + l3d])
1304
+ const l4e: u32 = ctSelect((l4 >>> 8) | (l4 << 24), 0xffffffff, 0)
1267
1305
  const l4a: i32 = toI32(l4 >>> 24) & 0xff
1268
- const l4b: i32 = toI32(l4 >>> 16) & 0xff
1269
- const l4c: i32 = toI32(l4 >>> 8) & 0xff
1306
+ const l4b: i32 = toI32(l4e >>> 8) & 0xff
1307
+ const l4c: i32 = toI32(l4e) & 0xff
1270
1308
  const l4d: i32 = toI32(l4) & 0xff
1271
- r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1272
- r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1273
- r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1274
- r3 ^= st[3126 + n] ^ (((st[3144 + l3a] + st[3400 + l3b]) ^ st[3656 + l3c]) + st[3912 + l3d])
1275
1309
  r4 ^= st[4168 + n] ^ (((st[4186 + l4a] + st[4442 + l4b]) ^ st[4698 + l4c]) + st[4954 + l4d])
1310
+ const r0e: u32 = ctSelect((r0 >>> 8) | (r0 << 24), 0xffffffff, 0)
1276
1311
  const r0a: i32 = toI32(r0 >>> 24) & 0xff
1277
- const r0b: i32 = toI32(r0 >>> 16) & 0xff
1278
- const r0c: i32 = toI32(r0 >>> 8) & 0xff
1312
+ const r0b: i32 = toI32(r0e >>> 8) & 0xff
1313
+ const r0c: i32 = toI32(r0e) & 0xff
1279
1314
  const r0d: i32 = toI32(r0) & 0xff
1315
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1316
+ const r1e: u32 = ctSelect((r1 >>> 8) | (r1 << 24), 0xffffffff, 0)
1280
1317
  const r1a: i32 = toI32(r1 >>> 24) & 0xff
1281
- const r1b: i32 = toI32(r1 >>> 16) & 0xff
1282
- const r1c: i32 = toI32(r1 >>> 8) & 0xff
1318
+ const r1b: i32 = toI32(r1e >>> 8) & 0xff
1319
+ const r1c: i32 = toI32(r1e) & 0xff
1283
1320
  const r1d: i32 = toI32(r1) & 0xff
1321
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1322
+ const r2e: u32 = ctSelect((r2 >>> 8) | (r2 << 24), 0xffffffff, 0)
1284
1323
  const r2a: i32 = toI32(r2 >>> 24) & 0xff
1285
- const r2b: i32 = toI32(r2 >>> 16) & 0xff
1286
- const r2c: i32 = toI32(r2 >>> 8) & 0xff
1324
+ const r2b: i32 = toI32(r2e >>> 8) & 0xff
1325
+ const r2c: i32 = toI32(r2e) & 0xff
1287
1326
  const r2d: i32 = toI32(r2) & 0xff
1327
+ l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1328
+ const r3e: u32 = ctSelect((r3 >>> 8) | (r3 << 24), 0xffffffff, 0)
1288
1329
  const r3a: i32 = toI32(r3 >>> 24) & 0xff
1289
- const r3b: i32 = toI32(r3 >>> 16) & 0xff
1290
- const r3c: i32 = toI32(r3 >>> 8) & 0xff
1330
+ const r3b: i32 = toI32(r3e >>> 8) & 0xff
1331
+ const r3c: i32 = toI32(r3e) & 0xff
1291
1332
  const r3d: i32 = toI32(r3) & 0xff
1333
+ l3 ^= st[3126 + n + 1] ^ (((st[3144 + r3a] + st[3400 + r3b]) ^ st[3656 + r3c]) + st[3912 + r3d])
1334
+ const r4e: u32 = ctSelect((r4 >>> 8) | (r4 << 24), 0xffffffff, 0)
1292
1335
  const r4a: i32 = toI32(r4 >>> 24) & 0xff
1293
- const r4b: i32 = toI32(r4 >>> 16) & 0xff
1294
- const r4c: i32 = toI32(r4 >>> 8) & 0xff
1336
+ const r4b: i32 = toI32(r4e >>> 8) & 0xff
1337
+ const r4c: i32 = toI32(r4e) & 0xff
1295
1338
  const r4d: i32 = toI32(r4) & 0xff
1296
- l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1297
- l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1298
- l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1299
- l3 ^= st[3126 + n + 1] ^ (((st[3144 + r3a] + st[3400 + r3b]) ^ st[3656 + r3c]) + st[3912 + r3d])
1300
1339
  l4 ^= st[4168 + n + 1] ^ (((st[4186 + r4a] + st[4442 + r4b]) ^ st[4698 + r4c]) + st[4954 + r4d])
1301
1340
  }
1302
1341
  const t0: u32 = r0
@@ -1352,65 +1391,77 @@ const bcryptBody6 = (st: u32[]): void => {
1352
1391
  l4 ^= st[4168]
1353
1392
  l5 ^= st[5210]
1354
1393
  for (let n: i32 = 1; n < 17; n += 2) {
1394
+ const l0e: u32 = ctSelect(l0 >>> 8, 0xffffffff, 0)
1355
1395
  const l0a: i32 = toI32(l0 >>> 24) & 0xff
1356
- const l0b: i32 = toI32(l0 >>> 16) & 0xff
1357
- const l0c: i32 = toI32(l0 >>> 8) & 0xff
1396
+ const l0b: i32 = toI32(l0e >>> 8) & 0xff
1397
+ const l0c: i32 = toI32(l0e) & 0xff
1358
1398
  const l0d: i32 = toI32(l0) & 0xff
1399
+ r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1400
+ const l1e: u32 = ctSelect(l1 >>> 8, 0xffffffff, 0)
1359
1401
  const l1a: i32 = toI32(l1 >>> 24) & 0xff
1360
- const l1b: i32 = toI32(l1 >>> 16) & 0xff
1361
- const l1c: i32 = toI32(l1 >>> 8) & 0xff
1402
+ const l1b: i32 = toI32(l1e >>> 8) & 0xff
1403
+ const l1c: i32 = toI32(l1e) & 0xff
1362
1404
  const l1d: i32 = toI32(l1) & 0xff
1405
+ r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1406
+ const l2e: u32 = ctSelect(l2 >>> 8, 0xffffffff, 0)
1363
1407
  const l2a: i32 = toI32(l2 >>> 24) & 0xff
1364
- const l2b: i32 = toI32(l2 >>> 16) & 0xff
1365
- const l2c: i32 = toI32(l2 >>> 8) & 0xff
1408
+ const l2b: i32 = toI32(l2e >>> 8) & 0xff
1409
+ const l2c: i32 = toI32(l2e) & 0xff
1366
1410
  const l2d: i32 = toI32(l2) & 0xff
1411
+ r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1412
+ const l3e: u32 = ctSelect(l3 >>> 8, 0xffffffff, 0)
1367
1413
  const l3a: i32 = toI32(l3 >>> 24) & 0xff
1368
- const l3b: i32 = toI32(l3 >>> 16) & 0xff
1369
- const l3c: i32 = toI32(l3 >>> 8) & 0xff
1414
+ const l3b: i32 = toI32(l3e >>> 8) & 0xff
1415
+ const l3c: i32 = toI32(l3e) & 0xff
1370
1416
  const l3d: i32 = toI32(l3) & 0xff
1417
+ r3 ^= st[3126 + n] ^ (((st[3144 + l3a] + st[3400 + l3b]) ^ st[3656 + l3c]) + st[3912 + l3d])
1418
+ const l4e: u32 = ctSelect(l4 >>> 8, 0xffffffff, 0)
1371
1419
  const l4a: i32 = toI32(l4 >>> 24) & 0xff
1372
- const l4b: i32 = toI32(l4 >>> 16) & 0xff
1373
- const l4c: i32 = toI32(l4 >>> 8) & 0xff
1420
+ const l4b: i32 = toI32(l4e >>> 8) & 0xff
1421
+ const l4c: i32 = toI32(l4e) & 0xff
1374
1422
  const l4d: i32 = toI32(l4) & 0xff
1423
+ r4 ^= st[4168 + n] ^ (((st[4186 + l4a] + st[4442 + l4b]) ^ st[4698 + l4c]) + st[4954 + l4d])
1424
+ const l5e: u32 = ctSelect(l5 >>> 8, 0xffffffff, 0)
1375
1425
  const l5a: i32 = toI32(l5 >>> 24) & 0xff
1376
- const l5b: i32 = toI32(l5 >>> 16) & 0xff
1377
- const l5c: i32 = toI32(l5 >>> 8) & 0xff
1426
+ const l5b: i32 = toI32(l5e >>> 8) & 0xff
1427
+ const l5c: i32 = toI32(l5e) & 0xff
1378
1428
  const l5d: i32 = toI32(l5) & 0xff
1379
- r0 ^= st[n] ^ (((st[18 + l0a] + st[274 + l0b]) ^ st[530 + l0c]) + st[786 + l0d])
1380
- r1 ^= st[1042 + n] ^ (((st[1060 + l1a] + st[1316 + l1b]) ^ st[1572 + l1c]) + st[1828 + l1d])
1381
- r2 ^= st[2084 + n] ^ (((st[2102 + l2a] + st[2358 + l2b]) ^ st[2614 + l2c]) + st[2870 + l2d])
1382
- r3 ^= st[3126 + n] ^ (((st[3144 + l3a] + st[3400 + l3b]) ^ st[3656 + l3c]) + st[3912 + l3d])
1383
- r4 ^= st[4168 + n] ^ (((st[4186 + l4a] + st[4442 + l4b]) ^ st[4698 + l4c]) + st[4954 + l4d])
1384
1429
  r5 ^= st[5210 + n] ^ (((st[5228 + l5a] + st[5484 + l5b]) ^ st[5740 + l5c]) + st[5996 + l5d])
1430
+ const r0e: u32 = ctSelect(r0 >>> 8, 0xffffffff, 0)
1385
1431
  const r0a: i32 = toI32(r0 >>> 24) & 0xff
1386
- const r0b: i32 = toI32(r0 >>> 16) & 0xff
1387
- const r0c: i32 = toI32(r0 >>> 8) & 0xff
1432
+ const r0b: i32 = toI32(r0e >>> 8) & 0xff
1433
+ const r0c: i32 = toI32(r0e) & 0xff
1388
1434
  const r0d: i32 = toI32(r0) & 0xff
1435
+ l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1436
+ const r1e: u32 = ctSelect(r1 >>> 8, 0xffffffff, 0)
1389
1437
  const r1a: i32 = toI32(r1 >>> 24) & 0xff
1390
- const r1b: i32 = toI32(r1 >>> 16) & 0xff
1391
- const r1c: i32 = toI32(r1 >>> 8) & 0xff
1438
+ const r1b: i32 = toI32(r1e >>> 8) & 0xff
1439
+ const r1c: i32 = toI32(r1e) & 0xff
1392
1440
  const r1d: i32 = toI32(r1) & 0xff
1441
+ l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1442
+ const r2e: u32 = ctSelect(r2 >>> 8, 0xffffffff, 0)
1393
1443
  const r2a: i32 = toI32(r2 >>> 24) & 0xff
1394
- const r2b: i32 = toI32(r2 >>> 16) & 0xff
1395
- const r2c: i32 = toI32(r2 >>> 8) & 0xff
1444
+ const r2b: i32 = toI32(r2e >>> 8) & 0xff
1445
+ const r2c: i32 = toI32(r2e) & 0xff
1396
1446
  const r2d: i32 = toI32(r2) & 0xff
1447
+ l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1448
+ const r3e: u32 = ctSelect(r3 >>> 8, 0xffffffff, 0)
1397
1449
  const r3a: i32 = toI32(r3 >>> 24) & 0xff
1398
- const r3b: i32 = toI32(r3 >>> 16) & 0xff
1399
- const r3c: i32 = toI32(r3 >>> 8) & 0xff
1450
+ const r3b: i32 = toI32(r3e >>> 8) & 0xff
1451
+ const r3c: i32 = toI32(r3e) & 0xff
1400
1452
  const r3d: i32 = toI32(r3) & 0xff
1453
+ l3 ^= st[3126 + n + 1] ^ (((st[3144 + r3a] + st[3400 + r3b]) ^ st[3656 + r3c]) + st[3912 + r3d])
1454
+ const r4e: u32 = ctSelect(r4 >>> 8, 0xffffffff, 0)
1401
1455
  const r4a: i32 = toI32(r4 >>> 24) & 0xff
1402
- const r4b: i32 = toI32(r4 >>> 16) & 0xff
1403
- const r4c: i32 = toI32(r4 >>> 8) & 0xff
1456
+ const r4b: i32 = toI32(r4e >>> 8) & 0xff
1457
+ const r4c: i32 = toI32(r4e) & 0xff
1404
1458
  const r4d: i32 = toI32(r4) & 0xff
1459
+ l4 ^= st[4168 + n + 1] ^ (((st[4186 + r4a] + st[4442 + r4b]) ^ st[4698 + r4c]) + st[4954 + r4d])
1460
+ const r5e: u32 = ctSelect(r5 >>> 8, 0xffffffff, 0)
1405
1461
  const r5a: i32 = toI32(r5 >>> 24) & 0xff
1406
- const r5b: i32 = toI32(r5 >>> 16) & 0xff
1407
- const r5c: i32 = toI32(r5 >>> 8) & 0xff
1462
+ const r5b: i32 = toI32(r5e >>> 8) & 0xff
1463
+ const r5c: i32 = toI32(r5e) & 0xff
1408
1464
  const r5d: i32 = toI32(r5) & 0xff
1409
- l0 ^= st[n + 1] ^ (((st[18 + r0a] + st[274 + r0b]) ^ st[530 + r0c]) + st[786 + r0d])
1410
- l1 ^= st[1042 + n + 1] ^ (((st[1060 + r1a] + st[1316 + r1b]) ^ st[1572 + r1c]) + st[1828 + r1d])
1411
- l2 ^= st[2084 + n + 1] ^ (((st[2102 + r2a] + st[2358 + r2b]) ^ st[2614 + r2c]) + st[2870 + r2d])
1412
- l3 ^= st[3126 + n + 1] ^ (((st[3144 + r3a] + st[3400 + r3b]) ^ st[3656 + r3c]) + st[3912 + r3d])
1413
- l4 ^= st[4168 + n + 1] ^ (((st[4186 + r4a] + st[4442 + r4b]) ^ st[4698 + r4c]) + st[4954 + r4d])
1414
1465
  l5 ^= st[5210 + n + 1] ^ (((st[5228 + r5a] + st[5484 + r5b]) ^ st[5740 + r5c]) + st[5996 + r5d])
1415
1466
  }
1416
1467
  const t0: u32 = r0